.NET Framework でも C# の最新機能を使う



## 確認環境
* .NET 8

`LangVersion` に `latest` を指定すると最新のC#バージョンになる。[デフォルトは `TargetFramework` で決まる。](https://learn.microsoft.com/ja-jp/dotnet/csharp/language-reference/configure-language-version#defaults)

```xml
  <PropertyGroup>
    <TargetFramework>net472</TargetFramework>
    <LangVersion>latest</LangVersion>
  </PropertyGroup>
```

以下は、C# 7.3 より後に実装された機能が .NET Framework 4.7.2 で使用できるかの調査結果。

https://learn.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-version-history


# 凡例

| 凡例 | 意味 |
| :-: | :- |
| ✅ | 対応 |
| ⚠️ | 部分的に対応 |
| ⛔ | 未対応 |
| ❔ | 影響なし |


# ⛔ 使用できない機能一覧

* 既定のインターフェイスメソッド
* ジェネリック数値演算
* `ref` フィールド
* 警告ウェーブ7
* インライン配列


# C# 8

## ✅ Readonly member: 読み取り専用メンバー

```cs
struct Foo
{
    private int _value;
    public readonly int GetValue() => _value;
}
```

## ⛔ Default interface methods: 既定のインターフェイスメソッド

ランタイムレベルの対応が必要なので使用不可。

```cs
interface IFoo
{
    int GetValue() => 1;
}
```

```
error CS8701: ターゲット ランタイムは、既定のインターフェイスの実装をサポートしていません。
```


## ✅ Pattern matching enhancements: パターンマッチングの拡張

### ✅ switch式

```cs
var x = 1;
var r = x switch
{
    0 => '0',
    1 => '1',
    _ => '?',
};
```

### ✅ プロパティーパターン

```cs
var x = DateTime.Now;
var r = x is { Year: 2023, Month: 12 };
```

### ✅ 位置指定パターン

```cs
var x = (1, 2);
var r = x switch
{
    (0, 0) => 0,
    (1, 2) => 1,
    _ => -1,
};
```


## ✅ Using declarations: using 宣言

```cs
using var stream = new MemoryStream();
```


## ✅ Static local functions: 静的ローカル関数

```cs
static int GetValue() => 1;
GetValue();
```


## ✅ Disposable ref structs: 破棄可能な ref 構造体

```cs
using var foo = new Foo();

ref struct Foo
{
    public void Dispose() { }
}
```


## ⚠️ Nullable reference types: Null 許容参照型

BCLの支援はないが、ユーザーコード内であれば問題なく使用できる。

```xml
  <PropertyGroup>
    <Nullable>enable</Nullable>
  </PropertyGroup>
```

```cs
string? x = null;
```

```cs
namespace System.Diagnostics.CodeAnalysis
{
    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
    public sealed class AllowNullAttribute : Attribute { }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property, Inherited = false)]
    public sealed class DisallowNullAttribute : Attribute { }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
    public sealed class MaybeNullAttribute : Attribute { }

    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, Inherited = false)]
    public sealed class NotNullAttribute : Attribute { }

    [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
    public sealed class MaybeNullWhenAttribute : Attribute
    {
        public MaybeNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
        public bool ReturnValue { get; }
    }

    [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
    public sealed class NotNullWhenAttribute : Attribute
    {
        public NotNullWhenAttribute(bool returnValue) => ReturnValue = returnValue;
        public bool ReturnValue { get; }
    }

    [AttributeUsage(AttributeTargets.Parameter | AttributeTargets.Property | AttributeTargets.ReturnValue, AllowMultiple = true, Inherited = false)]
    public sealed class NotNullIfNotNullAttribute : Attribute
    {
        public NotNullIfNotNullAttribute(string parameterName) => ParameterName = parameterName;
        public string ParameterName { get; }
    }

    [AttributeUsage(AttributeTargets.Method, Inherited = false)]
    public sealed class DoesNotReturnAttribute : Attribute { }

    [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
    public sealed class DoesNotReturnIfAttribute : Attribute
    {
        public DoesNotReturnIfAttribute(bool parameterValue) => ParameterValue = parameterValue;
        public bool ParameterValue { get; }
    }

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
    public sealed class MemberNotNullAttribute : Attribute
    {
        public MemberNotNullAttribute(string member) => Members = new[] { member };
        public MemberNotNullAttribute(params string[] members) => Members = members;
        public string[] Members { get; }
    }

    [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
    public sealed class MemberNotNullWhenAttribute : Attribute
    {
        public MemberNotNullWhenAttribute(bool returnValue, string member)
        {
            ReturnValue = returnValue;
            Members = new[] { member };
        }

        public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
        {
            ReturnValue = returnValue;
            Members = members;
        }

        public bool ReturnValue { get; }
        public string[] Members { get; }
    }
}
```


## ✅ Asynchronous streams: 非同期ストリーム

```cs
await foreach (var value in GetValuesAsync())
{
    Console.WriteLine(value);
}

async IAsyncEnumerable<int> GetValuesAsync()
{
    for (var i = 0; i < 10; i++)
    {
        await Task.Delay(1000);
        yield return i;
    }
}
```

```
dotnet add package Microsoft.Bcl.AsyncInterfaces
```


## ⚠️ Indices and ranges: インデックスと範囲

公式のパッケージが存在しない。

```cs
var arr = new byte[10];
var sub = arr[1..2];
```

非公式のパッケージ。

```
dotnet add package IndexRange
```

または自分で定義。

```cs
namespace System
{
    public readonly struct Index : IEquatable<Index>
    {
        private readonly int _value;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public Index(int value, bool fromEnd = false)
        {
            if (value < 0)
            {
                ThrowValueArgumentOutOfRange_NeedNonNegNumException();
            }

            if (fromEnd)
                _value = ~value;
            else
                _value = value;
        }

        private Index(int value)
        {
            _value = value;
        }

        public static Index Start => new Index(0);

        public static Index End => new Index(~0);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Index FromStart(int value)
        {
            if (value < 0)
            {
                ThrowValueArgumentOutOfRange_NeedNonNegNumException();
            }

            return new Index(value);
        }

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public static Index FromEnd(int value)
        {
            if (value < 0)
            {
                ThrowValueArgumentOutOfRange_NeedNonNegNumException();
            }

            return new Index(~value);
        }

        public int Value
        {
            get
            {
                if (_value < 0)
                    return ~_value;
                else
                    return _value;
            }
        }

        public bool IsFromEnd => _value < 0;

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public int GetOffset(int length)
        {
            int offset = _value;
            if (IsFromEnd)
            {
                offset += length + 1;
            }
            return offset;
        }

        public override bool Equals([NotNullWhen(true)] object? value) => value is Index && _value == ((Index)value)._value;

        public bool Equals(Index other) => _value == other._value;

        public override int GetHashCode() => _value;

        public static implicit operator Index(int value) => FromStart(value);

        public override string ToString()
        {
            if (IsFromEnd)
                return ToStringFromEnd();

            return ((uint)Value).ToString();
        }

        private static void ThrowValueArgumentOutOfRange_NeedNonNegNumException()
        {
            throw new ArgumentOutOfRangeException("value", "value must be non-negative");
        }

        private string ToStringFromEnd()
        {
            return '^' + Value.ToString();
        }
    }

    public readonly struct Range : IEquatable<Range>
    {
        public Index Start { get; }

        public Index End { get; }

        public Range(Index start, Index end)
        {
            Start = start;
            End = end;
        }

        public override bool Equals([NotNullWhen(true)] object? value) =>
            value is Range r &&
            r.Start.Equals(Start) &&
            r.End.Equals(End);

        public bool Equals(Range other) => other.Start.Equals(Start) && other.End.Equals(End);

        public override int GetHashCode()
        {
            return Start.GetHashCode() << 16 | End.GetHashCode();
        }

        public override string ToString()
        {
            return Start.ToString() + ".." + End.ToString();
        }

        public static Range StartAt(Index start) => new Range(start, Index.End);

        public static Range EndAt(Index end) => new Range(Index.Start, end);

        public static Range All => new Range(Index.Start, Index.End);

        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public (int Offset, int Length) GetOffsetAndLength(int length)
        {
            int start = Start.GetOffset(length);
            int end = End.GetOffset(length);

            if ((uint)end > (uint)length || (uint)start > (uint)end)
            {
                ThrowArgumentOutOfRangeException();
            }

            return (start, end - start);
        }

        private static void ThrowArgumentOutOfRangeException()
        {
            throw new ArgumentOutOfRangeException("length");
        }
    }
}
```

`RuntimeHelpers.GetSubArray` の定義。

```cs
namespace System.Runtime.CompilerServices
{
    public static partial class RuntimeHelpers
    {
        public static T[] GetSubArray<T>(T[] array, Range range)
        {
            if (array == null)
            {
                throw new ArgumentNullException(nameof(array));
            }

            (int offset, int length) = range.GetOffsetAndLength(array.Length);

            T[] dest;

            if (typeof(T[]) == array.GetType())
            {
                if (length == 0)
                {
                    return Array.Empty<T>();
                }

                dest = new T[length];
            }
            else
            {
                dest = (T[])Array.CreateInstance(array.GetType().GetElementType(), length);
            }

            Array.Copy(array, offset, dest, 0, length);

            return dest;
        }
    }
}
```


## ✅ Null-coalescing assignment: null 合体割り当て

```cs
string? v = null;
v ??= "";
```


## ✅ Unmanaged constructed types: 構築されたアンマネージド型

```cs
void Foo<T>() where T : unmanaged
{
}
```


## ✅ Stackalloc in nested expressions: 入れ子になった式の stackalloc

式の中で `stackalloc` が可能。ただしポインター `T*` ではなく `Span<T>` となる。

```cs
Foo(stackalloc int[1]);
void Foo(Span<int> p) { }
```

```
dotnet add package System.Memory
```


## ✅ Enhancement of interpolated verbatim strings: verbatim 補間文字列の拡張

```cs
var a = $@"";
var b = @$"";
```


# C# 9

## ✅ Records: レコード

```cs
record C(int Value, string Name);
```

`with` 式で変更。

```cs
var c1 = new C(1, "v1");
var c2 = c1 with { Value = 2 };
```


## ✅ Init only setters: init 専用セッター

```cs
class C
{
    public int Value { get; init; }
}
```

```cs
namespace System.Runtime.CompilerServices
{
    public static class IsExternalInit { }
}
```


## ✅ Top-level statements: 最上位レベルのステートメント

```cs
{ }
```


## ✅ Pattern matching enhancements: パターンマッチングの拡張

relational patterns: リレーショナルパターン `>` `<` `<=` `>=`

```cs
var x = 10;
var r = x switch
{
    < 1 => "low",
    > 5 => "hi",
    _ => "ok",
};
```

logical patterns: 論理パターン `and` `or` `not`

```cs
var x = 10;
var r = x switch
{
    < 1 or > 5 => "outer",
    > 2 and < 4 => "center",
    _ => "inner",
};

int? n = null;
if (n is not null) { }
```


## ✅ Performance and interop: パフォーマンスと相互運用

### ✅ Native sized integers: ネイティブサイズの整数
```cs
nint x = 1;
```


### ⚠️ Function pointers: 関数ポインター

```cs
unsafe
{
    delegate* managed<int> f1;
    delegate* unmanaged[Cdecl]<int> f2;
}
```

> ⛔ `delegate* unmanaged<int>` のように呼び出し規約を省略することはできない。


### ✅ Suppress emitting localsinit flag: localsinit フラグの出力を抑制

```cs
[SkipLocalsInit]
unsafe void Foo()
{
    var v = stackalloc byte[100];
}
```

```cs
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Module
        | AttributeTargets.Class
        | AttributeTargets.Struct
        | AttributeTargets.Interface
        | AttributeTargets.Constructor
        | AttributeTargets.Method
        | AttributeTargets.Property
        | AttributeTargets.Event, Inherited = false)]
    public sealed class SkipLocalsInitAttribute : Attribute { }
}
```


### ✅ Module initializers: モジュール初期化子

```cs
static class C
{
    [ModuleInitializer]
    internal static void M()
    {
    }
}
```

```cs
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
    public sealed class ModuleInitializerAttribute : Attribute { }
}
```


### ✅ New features for partial methods: 部分メソッドの新機能

戻り値が `void` ではない部分メソッドをサポート。

```cs
partial class C
{
    public partial int GetValue();
}

partial class C
{
    public partial int GetValue() => 1;
}
```


## ✅ Fit and finish features: 適合性と完成度の機能
### ✅ Target-typed new expressions: ターゲットにより型指定された new 式

```cs
List<int> list = new();
```


### ✅ static anonymous functions: 静的匿名関数

```cs
Func<int> f = static () => 1;
```


### ✅ Target-typed conditional expressions: ターゲットにより型指定された条件式

```cs
A a = true ? new A1() : new A2();

class A { }
class A1 : A { }
class A2 : A { }
```


### ❔ Covariant return types: 共変の戻り値の型

この機能は実装されていない。


### ✅ Extension GetEnumerator support for foreach loops: `foreach` で `GetEnumerator` 拡張メソッドに対応

```cs
foreach (var v in new C())
{
    Console.WriteLine(v);
}

class C
{
    public int V1 = 1;
    public int V2 = 2;
}

static class CExtensions
{
    public static CEnumerator GetEnumerator(this C c) => new(c);

    public struct CEnumerator : IEnumerator<int>
    {
        private readonly C c;
        private int index;

        public CEnumerator(C c)
        {
            this.c = c;
            this.index = 0;
        }

        public int Current => index == 1 ? this.c.V1 : index == 2 ? this.c.V2 : -1;

        object IEnumerator.Current => this.Current;

        public void Dispose() { }

        public bool MoveNext()
        {
            if (index is 0 or 1)
            {
                index++;
                return true;
            }

            return false;
        }

        public void Reset() => this.index = 0;
    }
}
```


### ✅ Lambda discard parameters: ラムダ式で破棄パラメーター

```cs
Action<int, string> f = (_, _) => { };
```

### ✅ Attributes on local functions: ローカル関数の属性

```cs
[MethodImpl(MethodImplOptions.AggressiveInlining)]
void Foo() {}
```


# C# 10

## ✅ Record structs: レコード構造体

```cs
record struct S();
readonly record struct ROS();
```


## ✅ Improvements of structure types: 構造体型の機能強化

引数なしコンストラクタに対応

```cs
struct S
{
    public S() { }
}
```

`with` 式で構造体型と匿名型に対応

```cs
var v1 = new S();
var v2 = v1 with { Value = 2 };

record struct S(int Value, string Name);
```


## ✅ Interpolated string handlers: 補間文字列ハンドラー

```cs
Foo($"aaa{123}");

void Foo(Handler handler) { }

[InterpolatedStringHandler]
ref struct Handler
{
    public Handler(int literalLength, int formattedCount) { }

    public void AppendLiteral(string value) { }
    public void AppendFormatted<T>(T value) { }
}
```

```cs
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
    public sealed class InterpolatedStringHandlerAttribute : Attribute
    {
        public InterpolatedStringHandlerAttribute() { }
    }
}
```


## ✅ global using directives: `global using` ディレクティブ

```cs
global using Foo.Bar;
```


### Implicit Usings

```xml
  <PropertyGroup>
    <ImplicitUsings>enable</ImplicitUsings>
  </PropertyGroup>

  <ItemGroup>
    <Using Remove="System.Net.Http" />
  </ItemGroup>

  <!-- or -->

  <ItemGroup>
    <PackageReference Include="System.Net.Http" Version="4.3.4" />
  </ItemGroup>
```

`System.Net.Http`が存在しないため、`Using`の`Remove`で除外するか、パッケージを参照必要がある。そうでない場合は下記のエラーとなる。

```
error CS0234: 型または名前空間の名前 'Http' が名前空間 'System.Net' に存在しません (アセンブリ参照があることを確認してください)
```


## ✅ File-scoped namespace declaration: ファイルスコープの名前空間宣言

```cs
namespace Foo.Bar;
```


## ✅ Extended property patterns: 拡張プロパティーパターン

入れ子のプロパティーパターンに対応

```cs
var r = x is { PropName1.PropName2: 2 };
```


## ✅ Improvements on lambda expressions: ラムダ式の機能強化

ラムダ式の自然型

```cs
var f = () => { }; // Action
```

ラムダ式の戻り値の型指定

```cs
var f = int () => 1;
```

ラムダ式の属性指定

```cs
var f3 = [return: NotNull] () => "";
```


## ✅ Allow const interpolated strings: 定数の補間文字列

```cs
const string S1 = "aaa";
const string S2 = "123";
const string S = $"{S1} {S2}";
```


## ✅ Record types can seal ToString(): レコード型で `ToString()` をシール

```cs
record C()
{
    public override sealed string ToString() => "aaa";
}
```


## ✅ Improved definite assignment: 限定代入の機能強化

null 状態分析が改善された。


## ✅ Allow both assignment and declaration in the same deconstruction: 同じ分解内の代入と宣言

```cs
var x = 0;
(x, int y) = (1, 2);
```


## ✅ Allow AsyncMethodBuilder attribute on methods: メソッドで `AsyncMethodBuilder` 属性を許可する

```cs
[AsyncMethodBuilder(typeof(TaskBuilder))]
async MyTask FooAsync() => await Task.Delay(0);

class TaskBuilder
{
    public static TaskBuilder Create() => new();
    public MyTask Task => new();
    public void SetException(Exception exception) { }
    public void SetResult() { }
    public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
        where TAwaiter : System.Runtime.CompilerServices.INotifyCompletion
        where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
    { }
    public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine)
          where TAwaiter : System.Runtime.CompilerServices.ICriticalNotifyCompletion
          where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
    { }
    public void Start<TStateMachine>(ref TStateMachine stateMachine)
          where TStateMachine : System.Runtime.CompilerServices.IAsyncStateMachine
    { }
    public void SetStateMachine(System.Runtime.CompilerServices.IAsyncStateMachine stateMachine) { }
}

class MyTask { }
```


## ✅ CallerArgumentExpression attribute: `CallerArgumentExpression` 属性

```cs
Foo(1.ToString() is { Length: 2 });

void Foo(bool condition, [CallerArgumentExpression(nameof(condition))] string? expression = null)
{
    if (!condition)
    {
        Console.WriteLine($"error: {expression}");
    }
}
```

```cs
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
    public sealed class CallerArgumentExpressionAttribute : Attribute
    {
        public CallerArgumentExpressionAttribute(string parameterName)
        {
            ParameterName = parameterName;
        }

        public string ParameterName { get; }
    }
}
```


## ✅ Enhanced #line pragma: 拡張 `#line` プラグマ

```cs
#line 100
Foo();
void Foo([CallerLineNumber] int lineNumber = 0) => Console.WriteLine(lineNumber);
```


# C# 11

https://learn.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-11

## ✅ Raw string literals: 未加工の文字リテラル

```cs
var s =
    """
    line1
    line2
    """;
```


## ⛔ Generic math support: ジェネリック数値演算サポート

静的抽象インターフェースメンバーに対応する必要がある。

```cs
var values = new int[] { 1, 2, 3 };
Console.WriteLine(Sum(values));

static T Sum<T>(IEnumerable<T> values)
    where T : INumber<T>
{
    T result = T.Zero;
    foreach (var value in values)
    {
        result += value;
    }
    return result;
}
```


## ✅ Generic attributes: ジェネリック属性

```cs
class G<T> : Attribute { }

[G<int>]
class C { }
```


## ✅ UTF-8 string literals: UTF-8 文字列リテラル

```cs
var data = "abc"u8;
```

```
dotnet add package System.Memory
```


## ✅ Newlines in string interpolation expressions: 文字列補間の改行

```cs
var s = $"aaa{
    123
}bbb";
```

## ✅ List patterns: リストパターン

```cs
var x = new int[] { 1, 2, 3, 4 };
var r = x is [ 1, 2, .. ];
```


## ✅ File-local types: ファイルローカル型

```cs
file class C { }
```

## ✅ Required members: 必須メンバー

```cs
var c1 = new C() { Value = 1 };
var c2 = new C(1);

class C
{
    public C() { }

    [SetsRequiredMembers]
    public C(int value)
    {
        this.Value = value;
    }

    public required int Value { get; set; }
}
```

```cs
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false, Inherited = false)]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public sealed class RequiredMemberAttribute : Attribute { }
}

namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
    public sealed class CompilerFeatureRequiredAttribute : Attribute
    {
        public CompilerFeatureRequiredAttribute(string featureName)
        {
            FeatureName = featureName;
        }
        public string FeatureName { get; }
        public bool IsOptional { get; init; }
        public const string RefStructs = nameof(RefStructs);
        public const string RequiredMembers = nameof(RequiredMembers);
    }
}

namespace System.Diagnostics.CodeAnalysis
{
    [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
    public sealed class SetsRequiredMembersAttribute : Attribute { }
}
```


## ✅ Auto-default structs: 自動デフォルト構造体

```cs
struct S
{
    int value;

    public S() { }
}
```


## ✅ Pattern match Span<char> on a constant string: 文字列定数で `Span<char>` とパターンマッチ

```cs
var s = "foo".AsSpan();
var r = s is "foo";
```


## ✅ Extended nameof scope: 拡張 `nameof` スコープ

```cs
[return: NotNullIfNotNull(nameof(test))]
string? Foo(string? test) => test is not null ? "not null" : null;
```


## ❔ Numeric IntPtr: 数値 `IntPtr`

.NET 7 以降のランタイムの場合、`nint` と `nuint` の特別扱いをしなくなる。（ジェネリック数値演算の関連）


## ⛔ ref fields: `ref` フィールド

```cs
ref struct S
{
    ref int value;
}
```


## ✅ scoped ref: `scoped ref` 変数

```cs
static Span<byte> Foo(scoped Span<byte> v) => new byte[1];
static Span<byte> Bar(Span<byte> v) => Foo(stackalloc byte[1]);
```


## ✅ Improved method group conversion to delegate: メソッドグループからデリゲートへの変換の改善

デリゲートにメソッドを渡すと毎回 `new` されていたが、キャッシュされるようになった。

```cs
Foo(A);
Foo(() => { });
static void Foo(Action a) => a();
static void A() { }
```


## ⛔ Warning wave 7: 警告ウェーブ7

小文字のみの型名を警告するコンパイラ警告の追加。

```xml
    <AnalysisLevel>7</AnalysisLevel>
    <TreatWarningsAsErrors>true</TreatWarningsAsErrors>
```

```cs
class foo { }
```

`net472` にすると警告が抑制されるようだ。


# C# 12

https://learn.microsoft.com/ja-jp/dotnet/csharp/whats-new/csharp-12

## ✅ Primary constructors: プライマリコンストラクター

```cs
class C(int value)
{
    int Value { get; } = value;
}
```


## ✅ Collection expressions: コレクション式

```cs
int[] values = [ 1, 2, 3 ];
```


## ✅ ref readonly parameters: `ref readonly` パラメーター

```cs
void Foo(ref readonly int value) {}
```


## ✅ Default lambda parameters: ラムダ式パラメーターの既定値

```cs
var f = (int value = 1) => { };
```


## ✅ Alias any type: 任意の型のエイリアス

```cs
using Alias = (int x, int y);
var value = new Alias(1, 2);
```


## ⛔ Inline arrays: インライン配列

ランタイムレベルの対応が必要。

```cs
[System.Runtime.CompilerServices.InlineArray(10)]
public struct Buffer
{
    private int _element0;
}
```

```cs
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Struct, AllowMultiple = false)]
    public sealed class InlineArrayAttribute : Attribute
    {
        public InlineArrayAttribute(int length)
        {
            Length = length;
        }
        public int Length { get; }
    }
}
```

```
error CS9171: ターゲットのランタイムはインライン配列型をサポートしていません。
```


## ✅ Experimental attribute: 試験段階の属性

```cs
[Experimental("Foo")]
void Foo() { }
```

```cs
namespace System.Diagnostics.CodeAnalysis
{
    [AttributeUsage(AttributeTargets.Assembly |
                    AttributeTargets.Module |
                    AttributeTargets.Class |
                    AttributeTargets.Struct |
                    AttributeTargets.Enum |
                    AttributeTargets.Constructor |
                    AttributeTargets.Method |
                    AttributeTargets.Property |
                    AttributeTargets.Field |
                    AttributeTargets.Event |
                    AttributeTargets.Interface |
                    AttributeTargets.Delegate, Inherited = false)]
    public sealed class ExperimentalAttribute : Attribute
    {
        public ExperimentalAttribute(string diagnosticId)
        {
            DiagnosticId = diagnosticId;
        }
        public string DiagnosticId { get; }
        public string? UrlFormat { get; set; }
    }
}
```

## ✅ Interceptors: インターセプター

```cs
new C().Foo();

class C
{
    public void Foo() => Console.WriteLine("Foo");
}

namespace MyInterceptors
{
    static class D
    {
        [InterceptsLocation(@"<FULL-PATH>\Program.cs", 9, 9)]
        public static void Bar(this C instance) => Console.WriteLine("Bar");
    }
}
```

```cs
namespace System.Runtime.CompilerServices
{
    [AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
#pragma warning disable CS9113
    public sealed class InterceptsLocationAttribute(string filePath, int line, int character) : Attribute
#pragma warning restore CS9113
    {
    }
}
```

```xml
  <PropertyGroup>
    <InterceptorsPreviewNamespaces>$(InterceptorsPreviewNamespaces);MyInterceptors</InterceptorsPreviewNamespaces>
  </PropertyGroup>
```