What's new in C# 10? A comprehensive overview of the latest features
C# 10.0 introduced several new features, including global usings, file-scoped namespaces, improved support for interpolated strings, improved support for lambda expressions, and more.
Here are some of the new features introduced in C# 10.0 with sample code:
- File-scoped namespaces
With file-scoped namespaces, you can define a namespace that applies to all code in a file, rather than having to add a namespace
declaration to every class in the file. Here's an example:
// MyFile.cs namespace MyNamespace; class MyClass { // ... }
- Global using directives
With global using directives, you can import a namespace across the entire project, without having to add a using
directive to every file that needs it. Here's an example:
// Program.cs
global using System;
global using System.Collections.Generic;
class Program
{
static void Main()
{
List myList = new();
myList.Add(1);
myList.Add(2);
Console.WriteLine(myList.Count);
}
}
- Interpolated string improvements
In C# 10.0, you can now use interpolated strings as format strings for string.Format
, and you can specify the format string using a new :
syntax. Here's an example:
int myNumber = 42;
string myString = $"{myNumber:0000}";
Console.WriteLine(myString); // outputs "0042"
- Improved lambda expressions
C# 10.0 introduces several improvements to lambda expressions, including the ability to use the and
and or
keywords instead of &&
and ||
. Here's an example:
List myList = new() { 1, 2, 3, 4, 5 };
List evenNumbers = myList.Where(x => (x % 2 == 0) and (x > 2)).ToList();
Console.WriteLine(string.Join(",", evenNumbers)); // outputs "4"
- Lambda expressions with block bodies
C# 10.0 allows you to use block bodies with lambda expressions. This can make your code more readable, especially for longer expressions. Here's an example:
Func<int, int> myFunc = x =>
{
int y = x * 2;
return y + 1;
};
Console.WriteLine(myFunc(5)); // outputs "11"
- Improved support for pattern matching
C# 10.0 introduces several improvements to pattern matching, including the ability to use patterns in more places, such as in switch
statements and in is
expressions. Here's an example:
int? myNumber = 42;
if (myNumber is int { } value)
{
Console.WriteLine($"The value is {value}.");
}
else
{
Console.WriteLine("The value is null.");
}
static abstract
members
C# 10.0 allows you to declare static abstract
members in abstract classes or interfaces. This can be useful if you want to provide a default implementation for a static
member, but still allow derived types to override it. Here's an example:
public abstract class MyBaseClass
{
public static abstract int MyStaticMethod();
}
public class MyDerivedClass : MyBaseClass
{
public static int MyStaticField = 42;
public static override int MyStaticMethod() => MyStaticField;
}
Console.WriteLine(MyDerivedClass.MyStaticMethod()); // outputs "42"
with
expressions for record types
C# 10.0 introduces the ability to use the with
keyword to create a new instance of a record type with some of its properties changed. Here's an example:
record MyRecord(string Name, int Age);
var myRecord = new MyRecord("Alice", 25);
var myNewRecord = myRecord with { Age = 30 };
Console.WriteLine(myNewRecord); // outputs "MyRecord { Name = Alice, Age = 30 }"
- Extended property patterns
C# 10.0 extends the support for property patterns, allowing you to match on the type of the property, as well as its value. Here's an example:
public class MyClass
{
public int MyProperty { get; set; }
}
var myObject = new MyClass { MyProperty = 42 };
if (myObject is { MyProperty: > 0, MyProperty: int myValue })
{
Console.WriteLine($"The value is {myValue}.");
}
- Implicit
new()
constraint for type parameters
C# 10.0 allows you to use the new()
constraint implicitly, without having to specify it explicitly in the type parameter list. Here's an example:
public class MyClass where T : new()
{
public void DoSomething()
{
T myObject = new();
// ...
}
}
async
method return types
C# 10.0 introduces the ability to use any type as the return type of an async
method, instead of being limited to Task
, Task<T>
, or ValueTask<T>
. This can be useful if you want to return a custom type from an async
method. Here's an example:
public async MyCustomType MyAsyncMethod()
{
// ...
}
- Improved support for interpolated verbatim strings
C# 10.0 introduces several improvements to interpolated verbatim strings, including the ability to use #
to escape curly braces in the format string. Here's an example:
string myString = @"First line
Second line
Third line: {myNumber:#,##0}";
int myNumber = 123456789;
Console.WriteLine(myString); // outputs "First line\nSecond line\nThird line: 123,456,789"
- Mixed assignment and declaration
C# 10.0 introduces the ability to mix declaration and assignment in a single statement, similar to how you can do in JavaScript or TypeScript. Here's an example:
int myNumber = 42, yourNumber = 7;
Console.WriteLine($"{myNumber}, {yourNumber}"); // outputs "42, 7"
14. Improved support for const
fields
C# 10.0 introduces several improvements to const
fields, including the ability to initialize them with non-constant expressions, and the ability to use them in attributes. Here's an example:
public class MyClass
{
public const int MyConstant = 42;
public const string MyString = "Hello, world!";
}
[Obsolete(MyClass.MyString)]
public void MyMethod()
{
// ...
}
15. Improved support for default expressions
C# 10.0 introduces several improvements to default
expressions, including the ability to use them in attribute arguments, and the ability to use them with the is
and as
operators. Here's an example:
[DefaultValue(default(int?))]
public int? MyNullableInt { get; set; }
object myObject = null;
if (myObject is default(int))
{
Console.WriteLine("This won't be executed.");
}