Top 11 features of C# 11.png

1️⃣ Raw String Literals

Novinka, na ktorú som osobne čakal asi najviac. Nová verziu C# umožňuje zadať ľubovoľný text bez nutnosti escapovania. Najčastejšie sa s tým stretávam keď potrebujem spraviť napríklad inplace JSON.

Použiť je potrebné aspoň tri dvojité úvodzovky """..""".

Ak máte text obsahujúci tri dvojité úvodzovky, musíte použiť štyri dvojité úvodzovky.

V kombinácii s string interpoláciou počet $ označuje, koľko po sebe idúcich zátvoriek začína a končí interpoláciu. V nižšie uvedenom príklade chcem použiť interpoláciu v JSON stringu, ktorý už obsahuje zložené zátvorky {}. To by bolo v rozpore s string interpolation, takže použijem dva $$ na označenie, že dvojité zátvorky `` začínajú a končia interpoláciu.

const string name = "Milan";
const string lastName = "Martiniak";

string json =
$$"""
{
    "Name": "",
    "LastName": ""
}
""";

Console.WriteLine(json);

Viac sa dočítate priamo v dokumentácií Strings - C# Programming Guide.

2️⃣ Newlines in String Interpolation Expressions

So stringom pokračujeme. Ďalšou užitočnou novinkou je možnosť využívať riadkovanie v string interpolácií. Doteraz to nebolo možné. Teraz to umožní použiť akýkoľvek valídny C# kód medzi {}, vrátane nových riadkov. Výrazne sa vylepší čitatelnosť zápisov.

const DayOfWeek day = DayOfWeek.Monday;

string dayInfo = $"Today is {day switch
{
    DayOfWeek.Saturday or DayOfWeek.Sunday => "weekend",
    _ => "working day"
}}.";

Console.WriteLine(dayInfo);

Interpolated string expression newline - C# 11.0 draft specifications

3️⃣ UTF-8 String Literals

C# 11 prináša UTF-8 string literals.

Zjednoduší to zápis konverzie UTF-8 znakov na ich bytovú reprezentáciu. Konverzia sa vykonáva v čase kompilácie.

// C# 10
//byte[] array = Encoding.UTF8.GetBytes("Hello UTF-8 String Literals");

// C# 11
ReadOnlySpan<byte> span = "Hello UTF-8 String Literals"u8;
byte[] array = "Hello UTF-8 String Literals"u8.ToArray();

4️⃣ Pattern match Span<char> on a constant string

Odteraz môžete použiť pattern matching na overenie či Span<char> obsahuje konkrétny string.

ReadOnlySpan<char> str = "World".AsSpan();

if (str is "World")
{
    Console.WriteLine("Hello world");
}

Dokumentácia Pattern match Span«char» - C# 11.0 draft specifications

5️⃣ An Unsigned Right-shift Operator

C# 11 prináša Right-shift Operator bez znamienka >>>.

Posúva bity doprava bez replikácie bitu vyššieho rádu pri každom posune.

int n = -32;
Console.WriteLine($"Before shift: bin = {Convert.ToString(n, 2),32}, dec = {n}");

int a = n >> 2;
Console.WriteLine($"After     >>: bin = {Convert.ToString(a, 2),32}, dec = {a}");

int b = n >>> 2;
Console.WriteLine($"After    >>>: bin = {Convert.ToString(b, 2),32}, dec = {b}");

// Output:
// Before shift: bin = 11111111111111111111111111100000, dec = -32
// After     >>: bin = 11111111111111111111111111111000, dec = -8
// After    >>>: bin =   111111111111111111111111111000, dec = 1073741816

Bitwise and shift operators - C# reference

6️⃣ Static Abstract Members in Interfaces

Do rozhraní môžete odteraz pridať statické abstraktné členy a definovať rozhrania, ktoré zahŕňajú preťažené operátory, iné statické členy a statické vlastnosti.

void NumberOfLegs<T>(T animal) where T: IAnimal {
    Console.WriteLine(T.NumberOfLegs);
}

NumberOfLegs(new Dog());
NumberOfLegs(new Snake());

public record Dog : IAnimal
{
    public static int NumberOfLegs => 4;
}

public record Snake : IAnimal
{
    public static int NumberOfLegs => 0;
}

public interface IAnimal
{
    static abstract int NumberOfLegs { get; }
}

Ok, viem toto nie je dobrý príklad 🙂, ale neodpustil som si. Reálnejšie použitie bude skôr na preťažovanie operátorov. Napríklad static abstract T operator +(T a, T b); a každý typ si potom definuje svoje správanie.

Viac info Explore static virtual members in interfaces

7️⃣ Extended nameof Scope

Názov parametra metódy môžete zadať v atribúte pri deklarácii metódy alebo parametra.

Toto je možné využiť napríkladpri pridávaní atribútov na analýzu kódu, alebo automatickom generovaní pomocou C# Source Generators.

public class MyAttribute : Attribute
{
    private readonly string _paramName;
    public MyAttribute(string paramName)
    {
        _paramName = paramName;
    }
}
public class MyClass
{
    [My(nameof(param))]
    public void Method(int param, [My(nameof(param))] int anotherParam)
    { }
}

Detail návrhu Extended nameof parameter scope - C# 11.0 draft specifications

8️⃣ Required Members

C# 11 prináša nový required modifikátor pre vlastnosti na vynútenie zadania pri inicializácií. Ak inicializujete objekt s chýbajúcou požadovanou vlastnosťou, zobrazí sa chyba kompilácie.

public class Person
{
    public Guid Id { get; set; } = Guid.NewGuid();
    public required string Name { get; set; }
    public required string LastName { get; set; }
}

// Initializations with required properties - valid
var p1 = new Person { Name = "Milan", LastName = "Martiniak" };
Person p2 = new("Milan", "Martiniak");

// Initializations with missing required properties - compilation error
var p3 = new Person { Name = "Milan" };
Person p4 = new();

Ak máte niekoľko parametrických konštruktorov, mali by ste pridať atribút SetsRequiredMembers na konštruktor, ktorý inicializuje všetky požadované vlastnosti.

public class Person
{
    public Person() { }

    [SetsRequiredMembers]
    public Person(string name, string lastName)
    {
        Name = name;
        LastName = lastName;
    }

    public Guid Id { get; set; } = Guid.NewGuid();
    public required string Name { get; set; }
    public required string LastName { get; set; }
}

Dokumentácia required modifier - C# Reference

9️⃣ Auto-default Structs

Ak ste pred C# 11 deklarovali štruktúru s konštruktorom, museli ste priradiť každú vlastnosť. V opačnom prípade ste dostali kompilačnú chybu.

[CS0171] Field 'PersonStruct.Age' must be fully assigned before control is returned to the caller.

V aktuálnej verzii kompilátor nastaví default hodnoty pre vynechané vlastnosti.

struct PersonStruct
{
    public PersonStruct(string name)
    {
        Name = name;
    }

    public string Name { get; set; }
    public int Age { get; set; }
}

Viac v špecifikácií Structure types - C# reference

🔟 List Patterns

C# 11 rozširuje aktuálny pattern matching o možnosť zisťovania zhody sekvencie elementov v poliach a listoch.

var numbers = new[] { 1, 2, 3, 4 };

// List and constant patterns
Console.WriteLine(numbers is [1, 2, 3, 4]); // True
Console.WriteLine(numbers is [1, 2, 4]);    // False

// List and discard patterns
Console.WriteLine(numbers is [_, 2, _, 4]); // True
Console.WriteLine(numbers is [.., 3, _]);   // True

// List and logical patterns
Console.WriteLine(numbers is [_, >= 2, _, _]); // True

Dokumentácia: Patterns - C# reference

1️⃣1️⃣ Generic Attributes

Pokiaľ ste potrebovali v staršej verzii C# do atribútu poslať typ, tak ste to museli spraviť pomocou konštruktora a následne využiť typeof expression.

V C# 11 už môžete pekne vytvoriť generický atribút.

// Before C# 11:
public class TypeAttribute : Attribute
{
    public TypeAttribute(Type type) => ParamType = type;

    public Type ParamType { get; }
}

public class GenericAttribute<T> : Attribute
{
}

// After C#
[Generic<int>]
public class MyType
{
    [Generic<int>()]
    public void Method() {}
}

Generic Attributes

Zdroje

Repositár s príkladmi