﻿# What's new in C\# 10: overview

This article covers the new version of the C\# language \- C\# 10\. Compared to C\# 9, C\# 10 includes a short list of enhancements\. Below we described the enhancements and added explanatory code fragments\. Let's look at them\.

![0875_OverviewCSharp10/image1.png](https://import.viva64.com/docx/blog/0875_OverviewCSharp10/image1.png)

## Enhancements of structure types

### Initialization of field structure

Now you can set initialization of fields and properties in structures:

```cpp
public struct User
{
    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }
    string Name { get; set; } = string.Empty;
    int Age { get; set; } = 18;
}
```

### Parameterless constructor declaration in a structure type

Beginning with C\# 10, you can declare a parameterless constructor in structures:

```cpp
public struct User
{
    public User()
    {

    }

    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }

    string Name { get; set; } = string.Empty;
    int Age { get; set; } = 18;
}
```

**Important\.** You can use parameterless constructors only if all fields and/or properties have initializers\. For example, if you do not set the _Age_ initializer, a compiler will issue an error:

_Error CS0843: Auto\-implemented property 'User\.Age' must be fully assigned before control is returned to the caller\._

## Applying the with expression to a structure

Before, you could use the _with_ expression with records\. With C\#10, you can use this expression with structures\. Example:

```cpp
public struct User
{
    public User()
    {

    }

    public User(string name, int age)
    {
        Name = name;
        Age = age;
    }

    public string Name { get; set; } = string.Empty;
    public int Age { get; set; } = 18;
}

User myUser = new("Chris", 21);
User otherUser = myUser with { Name = "David" };
```

It is clear that the property that we are changing \(in this case, the _Name_ field\) must have a public access modifier\.

## Global using

Beginning with C\# 10, you can use the _using_ directive across an entire project\. Add the _global_ keyword before the _using_ phrase:

```cpp
global using "Library name"
```

Thus, the _using_ directive allows you not to duplicate the same namespaces in different files\.

**Important\.** Use _global using_ construction BEFORE code lines that include _using_ without _global_ keyword\. Example:

```cpp
global using System.Text;
using System;
using System.Linq;
using System.Threading.Tasks;
// Correct code fragment
```

Otherwise:

```cpp
using System;
using System.Linq;
using System.Threading.Tasks;
global using System.Text;
// Error CS8915
// A global using directive must precede
// all non-global using directives.
```

If you wrote the namespace that was previously written with the _global_ keyword, the IDE will warn you \(_IDE: 0005: Using directive is unnecessary_\)\.

## File\-scoped namespace 

Sometimes you need to use the namespace within the entire file\. This action may shift the tabs to the right\. To avoid this problem, you can now use the _namespace_ keyword\. Write the _namespace_ keyword without braces:

```cpp
using System;
using System.Linq;
using System.Threading.Tasks;

namespace TestC10;

public class TestClass
{
    ....
}
```

Before C\# 10, it was necessary to keep _namespace_ braces open on the entire file:

```cpp
using System;
using System.Linq;
using System.Threading.Tasks;

namespace TestC10
{
    public class TestClass
    {
        ....
    }
}
```

Clearly, you can declare only one _namespace_ in the file\. Accordingly, the following code fragment is incorrect:

```cpp
namespace TestC10;
namespace MyDir;
// Error CS8954
// Source file can only contain
// one file-scoped namespace declaration
```

as well as the following piece of code:

```cpp
namespace TestC10;
namespace MyDir
{
    ....
}
// Error CS8955
// Source file can not contain both
// file-scoped and normal namespace declarations.
```

## Record enhancements 

### The class keyword

C\# 10\.0 introduces the optional keyword \- _class_\. The class keyword helps understand whether a record is of a reference type\.

Therefore, the two following records are identical:

```cpp
public record class Test(string Name, string Surname);
public record Test(string Name, string Surname);
```

### Record structs

Now it's possible to create record structs:

```cpp
record struct Test(string Name, string Surname)
```

By default, the properties of the _record struct_ are mutable, unlike the standard _record_ that have _init_ modifier\.

```cpp
string Name { get; set; }
string Surname { get; set; }
```

We can set the _readonly_ property to the record struct\. Then access to the fields will be equivalent to the standard record:

```cpp
readonly record struct Test(string Name, string Surname);
```

where the properties are written as:

```cpp
string Name { get; init; }
string Surname { get; init; }
```

The equality of two record struct objects is similar to the equality of two structs\. Equality is true if these two objects store the same values:

```cpp
var firstRecord = new Person("Nick", "Smith");
var secondRecord = new Person("Robert", "Smith");
var thirdRecord = new Person("Nick", "Smith");

Console.WriteLine(firstRecord == secondRecord);
// False
Console.WriteLine(firstRecord == thirdRecord);
// True
```

Note that the compiler doesn't synthesize a copy constructor for record struct types\. If we create a copy constructor and use the _with_ keyword when initializing a new object, then the assignment operator will be called instead of the copy constructor \(as it happens when working with the _record class_\)\.

### Seal the ToString\(\) method on records

As my colleague wrote in the article on the [enhancements for C\# 9](https://pvs-studio.com/en/blog/posts/csharp/0860/) , records have the overridden _toString_ method\. There is an interesting point about inheritance as related to this method\. The child objects cannot inherit the overriden _toString_ method from the parent record\. C\# 10 introduces the _sealed_ keyword so that the child objects can inherit the _ToString_ method\. This keyword prevents the compiler from synthesizing the _ToString_ implementation for any derived records\. Use the following keyword to override the _ToString_ method:

```cpp
public sealed override string ToString()
{
    ....
}
```

Let's create a record that tries to override the _toString_ method:

```cpp
public record TestRec(string name, string surname)
{
    public override string ToString()
    {
        return $"{name} {surname}";
    }
}
```

Now let's inherit the second record:

```cpp
public record InheritedRecord : TestRec
{
    public InheritedRecord(string name, string surname)
    :base(name, surname)
    {

    }
}
```

Now let's create an instance of each record and type the result to the console:

```cpp
TestRec myObj = new("Alex", "Johnson");
Console.WriteLine(myObj.ToString());
// Alex Johnson

InheritedRecord mySecObj = new("Thomas", "Brown");
Console.WriteLine(mySecObj.ToString());
// inheritedRecord { name = Thomas, surname = Brown}
```

As we can see, the _InheritedRecord_ did not inherit the _toString_ method\.

Let's slightly change the _TestRec_ record and add the _sealed_ keyword:

```cpp
public record TestRec(string name, string surname)
{
    public sealed override string ToString()
    {
        return $"{name} {surname}";
    }
}
```

Now let's re\-create two instances of the records and type the result to the console:

```cpp
TestRec myObj = new("Alex", "Johnson");
Console.WriteLine(myObj.ToString());
// Alex Johnson

InheritedRecord mySecObj = new("Thomas", "Brown");
Console.WriteLine(mySecObj.ToString());
// Thomas Brown
```

And\.\. woohoo\! The _InheritedRecord_ inherited the _toString_ method from the _TestRec_\.

## Easier access to nested fields and properties of property patterns

C\# 8\.0 introduced the property pattern that allows you to easily match on fields and/or properties of an object with the necessary expressions\.

Before, if you needed to check any nested property, the code could look too cumbersome: 

```cpp
....{property: {subProperty: pattern}}....
```

With C\#10, you just need to add the dots between the properties:

```cpp
....{property.subProperty: pattern}....
```

Let's see the change using the example of the method of taking the first 4 symbols of the name\.

```cpp
public record TestRec(string name, string surname);

string TakeFourSymbols(TestRec obj) => obj switch
{
    // old way:
    //TestRec { name: {Length: > 4} } rec => rec.name.Substring(0,4),

    // new way:
    TestRec { name.Length: > 4 } rec => rec.name.Substring(0,4),
    TestRec rec => rec.name,
};
```

The example above shows that the new type of property access is simpler and clearer than before\.

## Constant interpolated strings

Before, this feature was not supported\. C\# 10 allows you to use string interpolation for constant strings:

```cpp
const string constStrFirst = "FirstStr";
const string summaryConstStr = $"SecondStr {constStrFirst}";
```

**Interesting fact\.** This change relates only to string interpolation for constant strings, i\.e the addition of a constant character is not allowed:

```cpp
const char a = 'a';
const string constStrFirst = "FirstStr";
const string summaryConstStr = $"SecondStr {constStrFirst} {a}";
// Error CS0133
// The expression being assigned to
// 'summaryConstStr' must be constant
```

## Assignment and declaration in same deconstruction

In earlier versions of C\#, a deconstruction could assign values to EITHER declared variables \(all are declared\), OR variables that we initialize during calling \(all are NOT declared\):

```cpp
Car car = new("VAZ 2114", "Blue");

var (model, color) = car;
// Initialization

string model = string.Empty;
string color = string.Empty;
(model, color) = car;
// Assignment
```

The new version of the language allows simultaneous use of both previously declared and undeclared variables in deconstruction:

```cpp
string model = string.Empty;
(model, var color) = car;
// Initialization and assignment
```

The following error occurred in the C\#9 version:

_Error CS8184: A deconstruction cannot mix declarations and expressions on the left\-hand\-side\._

## Conclusion

As mentioned earlier, the list of changes is not as large as in the C\#9 version\. Some changes simplify the work, while others provide previously unavailable features\. The C\# is still evolving\. We're looking forward for new updates of the C\# language\.

Haven't read about new C\# 9 features yet? Check them out in our separate [article](https://pvs-studio.com/en/blog/posts/csharp/0860/)\.

If you want to see the original source, you can read the [Microsoft documentation](https://docs.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10)\.