C# 12 Features: A New Era of Simplicity and Elegance

Seyyid Yiğit
Huawei Developers
Published in
7 min readOct 17, 2023
Created by Bing AI

Introduction

C# is a popular and powerful programming language that has been evolving and improving over the years. 🚀 The latest version, C# 12, introduces some new features that make the language simpler and more elegant while maintaining its power of expression and performance. 😍

In this article, we will explore some of these new features and see how they can improve our code quality and productivity. 🙌

As a developer, I’m always excited to learn about new features that can make my life easier and my code better. 😊 That’s why I’m happy to share with you some of the new features in C# 12 that I think are awesome and useful. 😎

Let’s dive into each of these features and see how they work and why they are cool. 😊

Primary Constructors

Primary constructors are a new feature in C# 12 that let us declare parameters for the constructor right in the class or struct header. This can make our code simpler and cleaner. 😊 For example, suppose we have a class that represents a person with two properties: name and age. In previous versions of C#, we would have to write something like this:

public class Person
{
private string name;
private int age;

public Person(string name, int age)
{
this.name = name;
this.age = age;
}
public string Name
{
get { return name; }
}
public int Age
{
get { return age; }
}
}

That’s a lot of code for such a simple class, don’t you think? 😅 With primary constructors, we can write the same class in a much shorter way:

public class Person(string name, int age)
{
// name and age are in scope for the entire class
public string Name => name;
public int Age => age;
}

Wow, that’s much better! 😍 As a developer, I love how primary constructors can help me create simple and elegant classes or structs with less code and hassle. 😎 They also make our code more readable and maintainable by eliminating unnecessary boilerplate and repetition. 🙌

Let me explain how primary constructors work and why they are cool. 😊 The difference between primary constructors and previous constructors is that primary constructors allow us to declare the parameters in the class or struct header, instead of in a separate constructor body. This means that the parameters are in scope for the entire class or struct, and we can use them to initialize properties, fields, or other members. We can also use modifiers such as public, private, read-only, or ref on the parameters to control their accessibility and mutability.

We should use primary constructors when we want to create simple classes or structs that have a few properties or fields that are initialized by the constructor parameters. Primary constructors are especially useful for creating record types, which are immutable types that represent data with value semantics.

**Also, Entity Framework Core requires that your entity types have a parameterless constructor. This is because EF Core uses reflection to create instances of your entities when querying from the database.

But this structure doesn’t allow you to use a parameterless constructor for now. So, you shouldn’t use it on your own entity class.

Inline Arrays

Inline arrays are a new feature in C# 12 that lets us create an array without using the new keyword or specifying the array size. We can use the syntax [e1, e2, e3, etc.] to create an array of any type. The compiler will figure out the type and size of the array from the elements.

In previous versions of C#, you had to use the new keyword and specify the array size every time you wanted to create an array. This was tedious and error prone. For example, you had to write something like this:

int[] a = new int[3] {1, 2, 3};
string[] b = new string[3] {"a", "b", "c"};
object[] c = new object[2] {a, b};

That’s a lot of code for such simple arrays, don’t you think? 😅 And what if you wanted to create an array of a different type or size? You had to change the type and size in multiple places. But now, with C# 12, I can write the same thing as this: 😍

var a = [1, 2, 3]; // int[] of size 3
var b = ["a", "b", "c"]; // string[] of size 3
var c = [a, b]; // object[] of size 2

// create a 2 D array from variables:
int[] row0 = [1, 2, 3];
int[] row1 = [4, 5, 6];
int[] row2 = [7, 8, 9];
int[][] twoDFromVariables = [..row0, ..row1, ..row2];
// Create a span
Span<int> b = ['a', 'b', 'c', 'd', 'e', 'f', 'h', 'i'];

Isn’t that cool? 😎 They make our code more flexible and dynamic by allowing me to create arrays of any type without knowing the type or size in advance. 🙌

I hope you learned something new about inline arrays. They are one of my favorite features in C# 12. They make coding more fun and easier. 😍

Aliases

Aliases are a new feature in C# 12 that lets us create a new name for an existing type using the alias keyword. This can improve readability and avoid name conflicts. For example, suppose we have two types that represent points in two-dimensional and three-dimensional space. In previous versions of C#, we would have to write something like this:

using System.Drawing;
using System.Numerics;
Point p = new Point(10, 20); // System.Drawing.Point
Vector3 p3 = new Vector3(10, 20, 30); // System.Numerics.Vector3

That’s not very clear or consistent, is it? 😕 With aliases, we can write the same code in a more elegant way:

alias Point = System.Drawing.Point;
alias Point3D = System.Numerics.Vector3;
Point p = new Point(10, 20); // Point
Point3D p3 = new Point3D(10, 20, 30); // Point3D

That’s much better! 😍 The difference between aliases and previous types is that aliases allow us to create a new name for an existing type using the alias keyword. This means that we can use the new name anywhere we would use the original type, and the compiler will treat them as the same type. We can also use modifiers such as public, internal, partial, or ref on the aliases to control their accessibility and mutability.

We should use aliases when we want to create simple names for types that are otherwise hard to read or write. Aliases are especially useful for creating names for types that are frequently used in our code, such as system types, library types, or domain-specific types.

Optional Parameters in Lambda Expressions

Optional parameters in lambda expressions are a new feature in C# 12 that lets us omit the parameter type or name when it is not used in the lambda body. This can make the lambda expression more concise and expressive. For example, suppose we have a function that takes a lambda expression as an argument. In previous versions of C#, we would have to write something like this:

Func<int, int, int> add = (int x, int y) => x + y; // specify both parameter types and names
Func<int, int> _ = (int x) => x * x; // specify the parameter type
Func<int, int> increment = x => x + 1; // omit the parameter type

That’s not very consistent or elegant, is it? 😕 With optional parameters, we can write the same code in a more uniform way:

Func<int, int, int> add = (x, y) => x + y; // omit both parameter types
Func<int, int> square = x => x * x; // omit the parameter type
Func<int, int> increment = _ => _ + 1; // omit the parameter name

That’s much better! 😍 The difference between optional parameters and previous parameters is that optional parameters allow us to omit the parameter type or name using the _ symbol. This means that we can write less code and focus on the logic of the lambda expression. We can also use optional parameters with any kind of lambda expression, such as expression-bodied lambdas, statement-bodied lambdas, or async lambdas.

The advantage of optional parameters is that they can make our code more concise and expressive by eliminating unnecessary types and names. They can also make our code more flexible and dynamic by allowing us to write generic lambda expressions that can work with any type. We can use optional parameters to create lambda expressions for any purpose, such as delegates, events, LINQ queries, or tasks.

We should use optional parameters when we want to create simple lambda expressions that have a single parameter that is not used in the lambda body. Optional parameters are especially useful for creating lambda expressions that perform simple operations, such as arithmetic, logic, or comparison.

Conclusion

C# 12 is a new version of the language that introduces some new features that make the language simpler and more elegant. These features include primary constructors, inline arrays, aliases, and optional parameters in lambda expressions. These features can improve our code quality and productivity by making our code more readable, maintainable, concise, expressive, flexible, and dynamic. We can use these features to create simple and elegant classes or structs, arrays or collections, names or types, and lambda expressions or functions.

I hope this article helps you understand some of the new features in C# 12 better. 😊 Thank you for reading! 👋

--

--

Seyyid Yiğit
Huawei Developers

I’m a software engineer at Huawei. I love AI, fitness, gaming and sharing my projects and tips with you. Curious? Let’s learn together!