- Original: Introducing C# 10
- Author: Ken Bonny
- Translation: Rwing
- Translated article: [Translation] Preview of What’s New in C# 10
Earlier this week (note: the original article was published on May 1st), I followed a talk by Mads Torgersen at the DotNet SouthWest conference. He is the lead designer of the C# language at Microsoft. He outlined some cool new things that will be coming in C# 10. Let’s take a quick look.
A small disclaimer: most of these changes are already mostly complete. However, since it is still in active development, I cannot guarantee everything will be exactly as described when C# 10 ships.
struct record
The first thing he talked about is that the current implementation of record uses a class as the underlying object. Now a record struct variant will also be available, so the underlying type can be a value type. The difference is that a normal record passes references between functions, while a record struct passes copies of its value. The record struct will also support the with operator.
At the same time, operators can be added to records, and both record types support this.
record Person(string Name, string Email)
{
public static Person operator +(Person first, Person second)
{
// logic goes here
}
}
required
One of the goals the C# team is focusing on is making object initialization easier. This is why you can add the required modifier to properties of a class, struct, record, or struct record. It makes these properties mandatory. This can be achieved either through constructors or through object initializers. The following two class definitions are equivalent. If you add the required keyword, you cannot instantiate a Person without setting the Name property. The compiler will throw an error and prevent compilation.
class Person
{
public required string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
class Person
{
public Person(string name) => Name = name;
public string Name { get; set; }
public DateTime DateOfBirth { get; set; }
}
field
To further improve properties, it will be possible to completely eliminate the backing field. The new keyword field will provide access to the aforementioned field. It can be used for both setters and init-only properties.
class Person
{
public string Name { get; init => field = value.Trim(); }
public DateTime DateOfBirth { get; set => field = value.Date; }
}
with
There will also be some interesting small improvements in the next version. One of them is that anonymous types will also support the with operator.
var foo = new
{
Name = "Foo",
Email = "foo@mail.com"
};
var bar = foo with {Name = "Bar"};
namespace
Now it is possible to create a file with a namespace import and use that import anywhere. For example, if there is a very commonly used namespace that is used in almost every file, such as Microsoft.Extensions.Logging.ILogger, you can add a line global using Microsoft.Extensions.Logging.ILogger in any .cs file (I suggest in Program.cs or a dedicated Imports.cs), and then this namespace will be available throughout the entire project. Note that this does not apply to the entire solution! No one can predict which namespaces need to be imported, so they are grouped per project.
// Translator's note: The original text did not provide a code example; I added one for better understanding.
// Program.cs file
global using System;
// Sample.cs file
// No need to add "using System;" anymore
Console.WriteLine("foo");
Subsequently, there will also be an optimization for namespaces. Currently, namespaces require curly braces {} to wrap the code, meaning all code must be indented at least once. To save tabs (or four spaces) and screen space, adding a namespace anywhere in a file will make all code belong to that namespace. Research shows that in the vast majority of cases, all code in a file belongs to the same namespace. Using this approach reduces file size, which may not be noticeable for a solution (even if it contains thousands of files), but at the scale of GitHub/GitLab/BitBucket/... I think it will save them some space. If someone still wants to include multiple namespaces in one file, the option with curly braces will remain available.
// Traditional way LegacyNamespace.cs
namespace LegacyNamespace
{
class Foo
{
// legacy code goes here
}
}
// Simplified way SimplifiedNamespace.cs
namespace SimplifiedNamespace;
class Bar
{
// awesome code goes here
}
lambda
There will also be some cool updates to lambda expressions. The compiler will provide better support for inferring lambda signatures, and attributes can be added. You can explicitly specify the return type to help the compiler understand the lambda.
var f = Console.WriteLine;
var f = x => x; // inferred return type
var f = (string x) => x; // inferred signature
var f = [NotNull] x => x; // adding attributes
var f = [NotNull] (int x) => x;
var f = [return: NotNull] static x => x; // adding attributes
var f = T () => default; // explicit return type
var f = ref int (ref int x) => ref x; // using ref
var f = int (x) => x; // explicit return type for implicit input
var f = static void (_) => Console.Write("Help");
Thanks to Schooley for suggesting a less confusing feature example
interface
Finally, it will be possible to specify static methods and properties on interfaces. I know this will be a controversial topic, similar to adding default implementations to interfaces. I don’t like it. However, this could be very interesting. Imagine being able to specify a default value for an interface or specify a creation method.
interface IFoo
{
static IFoo Empty { get; }
static operator +(IFoo first, IFoo second);
}
class Foo : IFoo
{
public static IFoo Empty => new Foo();
public static operator +(IFoo first, IFoo second) => /* do calculation here */;
}
Personally, I like these changes. My favorites are the namespace changes and the improvements to interfaces. In short, the future is bright for C#. Hmm... (Translator's note: The author is playing with words here; the original text "the future is seeing sharp, see sharp" sounds like C#.)
Thanks, everyone. Bye for now.