C# 6 之後的新玩意隨便的學習筆記
===
Reference: [The history of C#](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-version-history)
## C# version and .NET version mapping
| Target | Version | C# language version default |
| --- | --- | --- |
| .NET | 7.x | C# 11 |
| .NET | 6.x | C# 10 |
| .NET | 5.x | C# 9.0 |
| .NET Core | 3.x | C# 8.0 |
| .NET Core | 2.x | C# 7.3 |
| .NET Standard | 2.1 | C# 8.0 |
| .NET Standard | 2.0 | C# 7.3 |
| .NET Standard | 1.x | C# 7.3 |
|.NET Framework | all | C# 7.3 |
## 6
### [Static imports](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/using-directive#static-modifier)
- The using static directive names a type whose static members and nested types you can access without specifying a type name. Its syntax is:
``` C#
using static <fully-qualified-type-name>;
```
e.g.
``` C#
using static System.Console;
using static System.Math;
class Program
{
static void Main()
{
WriteLine(Sqrt(3*3 + 4*4));
}
}
```
### [Exception filters](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/when)
- You use the when contextual keyword to specify a filter condition in the following contexts:
``` C#
catch (ExceptionType [e]) when (expr)
```
e.g.
``` C#
public static async Task<string> MakeRequest()
{
var client = new HttpClient();
var streamTask = client.GetStringAsync("https://localHost:10000");
try
{
var responseText = await streamTask;
return responseText;
}
catch (HttpRequestException e) when (e.Message.Contains("301"))
{
return "Site Moved";
}
catch (HttpRequestException e) when (e.Message.Contains("404"))
{
return "Page Not Found";
}
catch (HttpRequestException e)
{
return e.Message;
}
}
```
### Auto-property initializers
``` C#
public class Student
{
public string Name
{
{ get; set: } = "Akash"; // auto property initializer
}
}
```
### [Expression bodied members](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/lambda-operator#expression-body-definition)
``` C#
member => expression;
```
e.g.
``` C#
public override string ToString() => $"{fname} {lname}".Trim();
```
### [Null propagator](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#null-conditional-operators--and-)
* A null-conditional operator applies a member access (?.) or element access (?[]) operation to its operand only if that operand evaluates to non-null; otherwise, it returns null
e.g.
``` C#
A?.B?.Do(C);
A?.B?[C];
```
### [String interpolation using $](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/tokens/interpolated)
e.g.
``` C#
Console.WriteLine($"Hello, {name}! Today is {date.DayOfWeek}, it's {date:HH:mm} now.");
```
### [nameof expression (C# reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/nameof)
``` C#
Console.WriteLine(nameof(System.Collections.Generic)); // output: Generic
```
## 7
### [Tuples and deconstruction](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/value-tuples)
- 實際上是 [ValueTyuple](https://learn.microsoft.com/zh-tw/dotnet/api/system.valuetuple?view=net-7.0)
e.g.
```C#
(double, int) t1 = (4.5, 3);
Console.WriteLine($"Tuple with elements {t1.Item1} and {t1.Item2}.");
// Output:
// Tuple with elements 4.5 and 3.
(double Sum, int Count) t2 = (4.5, 3);
Console.WriteLine($"Sum of {t2.Count} elements is {t2.Sum}.");
// Output:
// Sum of 3 elements is 4.5.
var sum = 4.5;
var count = 3;
var t = (sum, count);
Console.WriteLine($"Sum of {t.count} elements is {t.sum}.");
```
### [Ref locals](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/declarations#reference-variables)
- 可以增加 performance 用
e.g.
``` C#
void Display(int[] s) => Console.WriteLine(string.Join(" ", s));
int[] xs = { 0, 0, 0 };
Display(xs);
ref int element = ref xs[0];
element = 1;
Display(xs);
element = ref xs[^1];
element = 3;
Display(xs);
// Output:
// 0 0 0
// 1 0 0
// 1 0 3
```
#### ref readonly
e.g.
``` C#
int[] xs = { 1, 2, 3 };
ref readonly int element = ref xs[0];
// element = 100; error CS0131: The left-hand side of an assignment must be a variable, property or indexer
Console.WriteLine(element); // output: 1
element = ref xs[^1];
Console.WriteLine(element); // output: 3
```
### ref return
e.g.
``` C#
using System;
public class NumberStore
{
private readonly int[] numbers = { 1, 30, 7, 1557, 381, 63, 1027, 2550, 511, 1023 };
public ref int GetReferenceToMax()
{
ref int max = ref numbers[0];
for (int i = 1; i < numbers.Length; i++)
{
if (numbers[i] > max)
{
max = ref numbers[i];
}
}
return ref max;
}
public override string ToString() => string.Join(" ", numbers);
}
public static class ReferenceReturnExample
{
public static void Run()
{
var store = new NumberStore();
Console.WriteLine($"Original sequence: {store.ToString()}");
ref int max = ref store.GetReferenceToMax();
max = 0;
Console.WriteLine($"Updated sequence: {store.ToString()}");
// Output:
// Original sequence: 1 30 7 1557 381 63 1027 2550 511 1023
// Updated sequence: 1 30 7 1557 381 63 1027 0 511 1023
}
}
```
## 7.2
### [private protected access modifier](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/private-protected)
- 可以用於某些設計的時候
e.g.
``` C#
public class BaseClass
{
private protected int myValue = 0;
}
public class DerivedClass1 : BaseClass
{
void Access()
{
var baseObject = new BaseClass();
// Error CS1540, because myValue can only be accessed by
// classes derived from BaseClass.
// baseObject.myValue = 5;
// OK, accessed through the current derived class instance
myValue = 5;
}
}
```
## 8
### [Readonly members](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/struct#readonly-instance-members)
- You can also use the readonly modifier to declare that an instance member doesn't modify the state of a struct. If you can't declare the whole structure type as readonly, use the readonly modifier to mark the instance members that don't modify the state of the struct.
- 看起來是給 compiler 最佳化用,可以有更好的 performance
### [Default interface methods](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/interface#default-interface-members)
- [Tutorial: Update interfaces with default interface methods](https://learn.microsoft.com/en-us/dotnet/csharp/advanced-topics/interface-implementation/default-interface-methods-versions)
- 設計上的用途可以參考這篇介紹,但看完還不是很懂在設計上的好處
e.g.
``` C#
interface IA
{
void M() { WriteLine("IA.M"); }
}
class C : IA { } // OK
IA i = new C();
i.M(); // prints "IA.M"
new C().M(); // error: class 'C' does not contain a member 'M'
```
### [Using declarations](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/using)
- 現在可以支援不用 brace 的 using
e.g.
``` C#
static IEnumerable<int> LoadNumbers(string filePath)
{
using StreamReader reader = File.OpenText(filePath);
var numbers = new List<int>();
string line;
while ((line = reader.ReadLine()) is not null)
{
if (int.TryParse(line, out int number))
{
numbers.Add(number);
}
}
return numbers;
}
```
- 支援多個 using 擺在一起
e.g.
``` C#
using (StreamReader numbersFile = File.OpenText("numbers.txt"), wordsFile = File.OpenText("words.txt"))
{
// Process both files
}
```
### [Nullable reference types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/nullable-reference-types)
- Project 要打開 `<nullable>` 的設定
- 設定值: https://learn.microsoft.com/en-us/dotnet/csharp/nullable-references#nullable-contexts
- 就是 reference type 也會需要補上 ? 如果那個 reference 是可能為 null 的話
e.g.
``` C#
string notNull = "Hello";
string? nullable = default;
notNull = nullable!; // null forgiveness
```
### [Asynchronous streams](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-8.0/async-streams)
- 有幾個非同步的 interface
- IAsyncDisposable
- IAsyncEnumerable / IAsyncEnumerator
- 就可以搭配語法
- `await foreach (var i in enumerable)`
### [Indices and ranges](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/member-access-operators#range-operator-)
- Index from end operator `^`
- 從最末端開始的 index 回來找
``` C#
int[] xs = new[] { 0, 10, 20, 30, 40 };
int last = xs[^1];
Console.WriteLine(last); // output: 40
var lines = new List<string> { "one", "two", "three", "four" };
string prelast = lines[^2];
Console.WriteLine(prelast); // output: three
string word = "Twenty";
Index toFirst = ^word.Length;
char first = word[toFirst];
Console.WriteLine(first); // output: T
```
- Range operator `..`
- 從 x 到 y
- 開頭結尾也可以只給一邊或都不給
- a.. is equivalent to a..^0
- ..b is equivalent to 0..b
- .. is equivalent to 0..^0
e.g.
``` C#
int[] numbers = new[] { 0, 10, 20, 30, 40, 50 };
int start = 1;
int amountToTake = 3;
int[] subset = numbers[start..(start + amountToTake)];
Display(subset); // output: 10 20 30
int margin = 1;
int[] inner = numbers[margin..^margin];
Display(inner); // output: 10 20 30 40
string line = "one two three";
int amountToTakeFromEnd = 5;
Range endIndices = ^amountToTakeFromEnd..^0;
string end = line[endIndices];
Console.WriteLine(end); // output: three
void Display<T>(IEnumerable<T> xs) => Console.WriteLine(string.Join(" ", xs));
```
### [Null-coalescing assignment](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/assignment-operator#null-coalescing-assignment)
- 除了 `??` 外多了一個 `??=` 的 operator
### [Unmanaged constructed types](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/unmanaged-types)
- Generic 多了一個 `unmanaged` 的限制條件可以使用
- 其他目的不明 @@
## 9
比爾叔整理的 [C# 9.0 搶先看](https://dotblogs.com.tw/billchung/Series?qq=C%23%209.0%20%E6%90%B6%E5%85%88%E7%9C%8B)
### [Records](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#record-types)
- A reference type
- 其實就是 record class
- 宣告語法
- [C# 9:Record 詳解](https://www.huanlintalk.com/2022/03/csharp-9-record-explained.html)
``` C#
// 1
public record Person(string FirstName, string LastName);
// 2
public record Person
{
public required string FirstName { get; init; }
public required string LastName { get; init; }
};
// 3
public record Person
{
public required string FirstName { get; set; }
public required string LastName { get; set; }
};
```
- While records can be mutable, they are primarily intended for supporting immutable data models. The record type offers the following features:
- Concise syntax for creating a reference type with immutable properties.
- 使用簡易語法預設建立的就是 immutable properties
- e.g. `public record Person(string FirstName, string LastName);`
- A record type is not necessarily immutable
- Behavior useful for a data-centric reference type:
- Value equality
- For types with the record modifier (record class, record struct, and readonly record struct), two objects are equal if they are of the same type and store the same values.
- Concise syntax for nondestructive mutation
- `with` keyword
``` C#
public record Person(string FirstName, string LastName)
{
public string[] PhoneNumbers { get; init; }
}
public static void Main()
{
Person person1 = new("Nancy", "Davolio") { PhoneNumbers = new string[1] };
Console.WriteLine(person1);
// output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
Person person2 = person1 with { FirstName = "John" };
Console.WriteLine(person2);
// output: Person { FirstName = John, LastName = Davolio, PhoneNumbers = System.String[] }
Console.WriteLine(person1 == person2); // output: False
person2 = person1 with { PhoneNumbers = new string[1] };
Console.WriteLine(person2);
// output: Person { FirstName = Nancy, LastName = Davolio, PhoneNumbers = System.String[] }
Console.WriteLine(person1 == person2); // output: False
person2 = person1 with { };
Console.WriteLine(person1 == person2); // output: True
}
```
- Built-in formatting for display
- 呼叫 `ToString` 的時候會依照 `Person { FirstName = Nancy, LastName = Davolio, ChildNames = System.String[] }` 來輸出
- Support for inheritance hierarchies
- 只有 `record class` 可以
- A record can inherit from another record. However, a record can't inherit from a class, and a class can't inherit from a record.
- Deconstructor behavior in derived records
e.g.
``` C#
public abstract record Person(string FirstName, string LastName);
public record Teacher(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public record Student(string FirstName, string LastName, int Grade)
: Person(FirstName, LastName);
public static void Main()
{
Person teacher = new Teacher("Nancy", "Davolio", 3);
var (firstName, lastName) = teacher; // Doesn't deconstruct Grade
Console.WriteLine($"{firstName}, {lastName}");// output: Nancy, Davolio
var (fName, lName, grade) = (Teacher)teacher;
Console.WriteLine($"{fName}, {lName}, {grade}");// output: Nancy, Davolio, 3
}
```
### [Init only setters](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#init-only-setters)
- 製作包含 class 自己也是 readonly 的 property
e.g.
``` C#
public struct WeatherObservation
{
public DateTime RecordedAt { get; init; }
public decimal TemperatureInCelsius { get; init; }
public decimal PressureInMillibars { get; init; }
public override string ToString() =>
$"At {RecordedAt:h:mm tt} on {RecordedAt:M/d/yyyy}: " +
$"Temp = {TemperatureInCelsius}, with {PressureInMillibars} pressure";
}
```
### [Support for code generators](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-9#support-for-code-generators)
* [Module initializers](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/module-initializers)
* [New features for partial methods](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-9.0/extending-partial-methods)
## 10
比爾叔整理的 [C# 10 新功能](https://dotblogs.com.tw/billchung/Series?qq=C%23%2010%20%E6%96%B0%E5%8A%9F%E8%83%BD)
### [Record structs](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10#record-structs)
- 新增了明確指定把 record 宣告成 struct 的支援,意思就是可以宣告 `struct record`
- [C# 10:Record 的改進](https://www.huanlintalk.com/2022/03/csharp-10-record-improvements.html)
### [Improvements of structure types](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10#improvements-of-structure-types)
- You can declare an instance parameterless constructor in a structure type and initialize an instance field or property at its declaration. For more information, see the Struct initialization and default values section of the Structure types article.
- `with` 的表達示的左邊可以是 structure type or an anonymous type.
``` C#
using System;
public class InheritanceExample
{
public record Point(int X, int Y);
public record NamedPoint(string Name, int X, int Y) : Point(X, Y);
public static void Main()
{
Point p1 = new NamedPoint("A", 0, 0);
Point p2 = p1 with { X = 5, Y = 3 };
Console.WriteLine(p2 is NamedPoint); // output: True
Console.WriteLine(p2); // output: NamedPoint { X = 5, Y = 3, Name = A }
}
}
```
- 可以在宣告 strcut 加上 `readonly` 的 keyword `readonly struct`
-
### [Global using directives](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10#global-using-directives)
- 支援 `global using <fully-qualified-namespace>;`
- 讓 using 的東西在 project 的所有的檔案都適用
- 也支援 `global using static`
- 也可以直接在 project 檔案加上 `<Using>` 的區段
- e.g. `<Using Include="My.Awesome.Namespace" />`
### [Lambda expression improvements](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10#lambda-expression-improvements)
- 可以正常推論 netrual type
- e.g. 現在這樣是支援的,= 的左邊不需要指定明確的型別
``` C#
object parse = (string s) => int.Parse(s); // Func<string, int>
Delegate parse = (string s) => int.Parse(s); // Func<string, int>
var read = Console.Read; // Just one overload; Func<int> inferred
var write = Console.Write; // ERROR: Multiple overloads, can't choose
LambdaExpression parseExpr = (string s) => int.Parse(s); // Expression<Func<string, int>>
Expression parseExpr = (string s) => int.Parse(s); // Expression<Func<string, int>>
```
- 資訊不足的狀態下仍然是無法得
- e.g. 下面這樣仍然是不行
``` C#
var parse = s => int.Parse(s); // ERROR: Not enough type info in the lambda
```
### [Record types can seal ToString](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10#record-types-can-seal-tostring)
- [C# 10:Record 的改進](https://www.huanlintalk.com/2022/03/csharp-10-record-improvements.html)
### [CallerArgumentExpression attribute diagnostics](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-10#callerargumentexpression-attribute-diagnostics)
- [CallerArgumentExpression](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/proposals/csharp-10.0/caller-argument-expression)
- 可以自動帶入 Caller 額外的描述,這個是帶入宣告時的參數名稱,建構子的參數代表的就是名稱
- e.g.
``` C#
public static void InRange(int argument, int low, int high,
[CallerArgumentExpression("argument")] string argumentExpression = null,
[CallerArgumentExpression("low")] string lowExpression = null,
[CallerArgumentExpression("high")] string highExpression = null)
{
if (argument < low)
{
throw new ArgumentOutOfRangeException(paramName: argumentExpression,
message: $"{argumentExpression} ({argument}) cannot be less than {lowExpression} ({low}).");
}
if (argument > high)
{
throw new ArgumentOutOfRangeException(paramName: argumentExpression,
message: $"{argumentExpression} ({argument}) cannot be greater than {highExpression} ({high}).");
}
}
```
- 同場加映 C# 5.0 就有的同樣目的的 attribute
- CallerMemberName
- CallerFilePath
- CallerLineNumber
- [Determine caller information using attributes interpreted by the C# compiler](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/attributes/caller-information#argument-expressions)
## 11
比爾叔整理的 [C# 11 新功能](https://dotblogs.com.tw/billchung/Series?qq=C%23%2011%20%E6%96%B0%E5%8A%9F%E8%83%BD)
### [Raw string literals](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#raw-string-literals)
- 字串支援前後 `"""` (就是三個 " )的宣告方式
- 在這個段落裡面的東西都會被當成字串來對待,包含換行等等
- 如果要在中間夾雜 `"` 也不需要用 `""` 就用單個即可
- 換行的開頭的 空白 都會被去掉
- e.g.
``` C#
string longMessage = """
This is a long message.
It has several lines.
Some are indented
more than others.
Some should start at the first column.
Some have "quoted text" in them.
""";
```
### [Generic math support](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-math-support)
- [Generic math](https://learn.microsoft.com/en-us/dotnet/standard/generics/math)
- [C# 11 新功能 -- static virtual members in interfaces and generic math](https://dotblogs.com.tw/billchung/2023/01/01/015634)
- 啟用了這些 featrue 來支援 generic math
- static virtual members in interfaces
- checked user defined operators
- relaxed shift operators
- unsigned right-shift operator
#### static virtual members in interfaces
#### [unsigned right-shift operator](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/bitwise-and-shift-operators#unsigned-right-shift-operator-)
- 新的 operator `>>>`
- The >>> operator always performs a logical shift. That is, the high-order empty bit positions are always set to zero, regardless of the type of the left-hand operand
``` C#
int x = -8;
Console.WriteLine($"Before: {x,11}, hex: {x,8:x}, binary: {Convert.ToString(x, toBase: 2), 32}");
int y = x >> 2;
Console.WriteLine($"After >>: {y,11}, hex: {y,8:x}, binary: {Convert.ToString(y, toBase: 2), 32}");
int z = x >>> 2;
Console.WriteLine($"After >>>: {z,11}, hex: {z,8:x}, binary: {Convert.ToString(z, toBase: 2).PadLeft(32, '0'), 32}");
// Output:
// Before: -8, hex: fffffff8, binary: 11111111111111111111111111111000
// After >>: -2, hex: fffffffe, binary: 11111111111111111111111111111110
// After >>>: 1073741822, hex: 3ffffffe, binary: 00111111111111111111111111111110
```
### [Generic attributes](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#generic-attributes)
``` C#
// Before C# 11:
public class TypeAttribute : Attribute
{
public TypeAttribute(Type t) => ParamType = t;
public Type ParamType { get; }
}
[TypeAttribute(typeof(string))]
public string Method() => default;
// C# 11 feature:
public class GenericAttribute<T> : Attribute { }
[GenericAttribute<string>()]
public string Method() => default;
```
### [UTF-8 string literals](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#utf-8-string-literals)
- .NET 原生使用 UTF-16 來儲存字串,現在可以指定使用 UTF-8
- UTF-8 string 不是 compile time 的常數,所以不可以當作 method 參數的預設值
``` C#
ReadOnlySpan<byte> AuthWithTrailingSpace = new byte[] { 0x41, 0x55, 0x54, 0x48, 0x20 };
ReadOnlySpan<byte> AuthStringLiteral = "AUTH "u8;
// 要把 utf-8 字串轉 byte[]
byte[] AuthStringLiteral = "AUTH "u8.ToArray();
```
### [Newlines in string interpolations](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#newlines-in-string-interpolations)
- 在 `$` 的字串裡面輸入變數的 `{` `}` 中間可以換行了
### [File local types](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#file-local-types)
- 新的 keyword `file`,可以用來宣告在這個檔案限定的東西
e.g.
``` C#
file class HiddenWidget
{
// implementation
}
```
- source generator 產生的 type 一般都會使用這個宣告
### [Required members](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#required-members)
- [C# 11 新功能 -- Required members](https://dotblogs.com.tw/billchung/2023/01/10/205859)
- 總之就是可以加上 `required` 的關鍵字,讓使用的時候在建構子一定要給值
- [required modifier (C# Reference)](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/required)
- 這邊有一些使用的細項規則,包含繼承之後 `required` 是不能被隱藏 (hide) 的等等
### [Extended nameof scope](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#extended-nameof-scope)
- 支援在 attribute 裡面使用 `nameof` 了
### [ref fields and ref scoped variables](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#ref-fields-and-ref-scoped-variables)
- [scoped ref](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/statements/declarations#scoped-ref)
- The contextual keyword scoped restricts the lifetime of a value. The scoped modifier restricts the ref-safe-to-escape or safe-to-escape lifetime, respectively, to the current method. Effectively, **adding the scoped modifier asserts that your code won't extend the lifetime of the variable.**
### [Improved method group conversion to delegate](https://learn.microsoft.com/en-us/dotnet/csharp/whats-new/csharp-11#improved-method-group-conversion-to-delegate)
- 主要是在使用 delegate 的"方式"的效能改進
- 看 [Improved method group conversion to delegate](https://prographers.com/blog/c-11-improved-method-group) 介紹比較好懂
## 其他待讀主題
- [Records](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/record)
- [Pattern matching](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/operators/patterns)
- [Source Generators](https://learn.microsoft.com/en-us/dotnet/csharp/roslyn-sdk/source-generators-overview)