Trusted answers to developer questions
Trusted Answers to Developer Questions

Related Tags

c#

What are fit and finish features in C# 9.0?

Hammad Nasir

Overview

This shot will summarize several features that have been improved for user convenience in C# 9.0.

new expression improvement

The new keyword in C# is used to instantiate an object of the specified type. The syntax of the new keyword is:

new typeName;

Here, the typeName can be any data type that can be instantiated (e.g. class, struct, etc.).

Assignment example

Let’s assume that we have a class named Person as follows:

public class Person {
  public string Name;
}

We would like to create an instance for it. Prior to C# 9.0, we will do this as follows:

Person p = new Person();

However, with the new improvement, we don’t need to specify the data type of the object as it can be automatically inferred from the context. Using this target-typed new approach, the above code can be written as follows:

Person p = new();

Moreover, we can also combine this approach with init only setters feature of C# to set the Name field’s value on initialization as follows:

Person p = new() { Name = "Hammad" };

Therefore, the Name field of the p object is set to Hammad on initialization.

Method call example

The target-typed new keyword approach also works with method calls. Let’s say we have the same Person class as before and we would like to invoke the following method with a new instance of the Person class:

public void SayHello(Person p) {
  System.Console.WriteLine(p.Name + " says hello!");
}

To call this method with a newly created instance of the Person class, we would write the following code:

SayHello(new Person() { Name = "Hello" });

However, the target-typed new approach allows us to omit the Person class since it’s automatically inferred. Hence, the code becomes the following:

SayHello(new() { Name = "Hello" });

Note: Target-typed new can also be used in return statements as well (e.g. return new();). In this case, the type of object is inferred by the return type of the method.

Conditional expression target type resolution improvement

The conditional expression is used when a value to be utilized in an expression is to be decided on based on a condition. The syntax of the conditional expression is as follows:

condition ? consequent : alternative

A common use case is to initialize a variable based on some condition. Following is an example:

bool even = true;
int? x = even ? 2 : 1;

The above code assigns the value 2 to the variable x since even is true.

Let’s extend the example above as follows:

bool even = false;
int? x = even ? 2 : (int?)null;

In this example, we change the value of even from true to false; therefore, null will be assigned to the variable x. However, since null is not an integer type, it needs to be explicitly cast to int (the (int?) preceding null does this job) for the conditional expression to work.

C# 9.0 has introduced target-typed resolution for conditional expressions. Therefore, if an implicit conversion to the target data type exists, the expression will work. Hence, the following code is re-written based on target-typed resolution of conditional expressions:

bool even = false;
int? x = even ? 2 : null;

Notice here that we didn’t precede null with (int?), since the compiler will automatically apply casting here.

static modifier improvement

The static modifier is usually associated with a class or a member (can be a method or a variable as well). However, C# 9.0 allows the static modifier to be used with lambda expressions and anonymous functions. This enables such functions to not capture the state of the enclosing scope and instance. This prevents unintentional capture of variables in the scope that don’t need to be used, improving security and performance.

The following code presents an example:

int x = 10;
Func <int> temp = () => x

The example above demonstrates that x is used inside the temp lambda expression even though it is not passed as an argument to temp. If we use the static modifier with the lambda expression, we won’t be able to do this. The following code presents an example:

int x = 10;
Func <int> temp = static () => 10

In the above example, we use the static modifier. We can’t reference the variable x, so the program returns the value 10 instead.

Discards as parameters to lambda expressions improvement

Lambda expressions

Lambda expressions are syntactically equivalent to arrow functions in other languages (e.g. JavaScript). Following is an example of a lambda expression:

Func<int, int, int> prodInt = (int x, int y) => {
  return x * y;
}

The above code defines a lambda expression that takes in 2 int parameters, x and y, and returns their product. This lambda expression is stored inside the prodInt variable that is of type Func<int, int, int> (the first 2 ints specify the datatype of the parameter, and the last int specifies the data type of the return value).

This lambda expression can also be written without data types for the parameters as follows (since they are inferred by the compiler):

Func<int, int, int> prodInt = (x, y) => {
  return x * y;
}

Moreover, if there is only 1 statement in the lambda expression, the return keyword and the curly braces can be excluded as follows:

Func<int, int, int> prodInt = (x, y) => x * y;

Discards in lambda expressions

The improvement in C# 9.0 is that parameters that are not to be used inside the lambda expression body but still get passed do not have to be named. This is done using a discard, which is basically an underscore (_).

Let’s say we make a new lambda expression prodInt2 that actually just multiples the first argument with itself as follows:

Func<int, int, int> prodIn2 = (x, y) => x * x;

Notice that we are still taking 2 arguments x and y. However, inside our lambda expression, we are only using the first argument x. To avoid naming our second argument, we simply use a discard in its place as follows:

Func<int, int, int> prodIn2 = (x, _) => x * x;

This relieves the programmer of the burden to name parameters that are not going to be used.

Note: This feature is useful when passing a lambda expression to provide an event handler.

RELATED TAGS

c#

CONTRIBUTOR

Hammad Nasir
Copyright ©2022 Educative, Inc. All rights reserved
RELATED COURSES

View all Courses

Keep Exploring