Novinky v jazyku C# 11
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() {}
}