C# 10 完全紹介

## 序論
.NET 6の開発進展を伴い、C# 10の最終機能もようやく決定され、デイリービルドに実装されました。今回の更新は主にタイプシステムの改善ために出来ました。じゃあ早速見に行こう。

## Structレコード
まずはstructレコードです。C# 10以前はレコードをstructに適用できませんでしたが、C# 10以降は可能になりました。

例えば、ある点の定義は以下のような書き方は可能です:

```csharp
record struct Point(int X, int Y);
```

そうすれば、`ToString`や`GetHashCode`や`Equals`は自動的に実装され、とても便利です。

## レコード型で`ToString`は`sealed`可能
レコードの型で`ToString`をオーバーライドするときに`sealed`修飾子を追加できます。`ToString`を`sealed`にすると、コンパイラで派生レコード型に対して`ToString`メソッドを生成できなくなります。 

```csharp
record class Foo
{
    public sealed override string ToString()
    {
        // ...
    }
}
```

## Structでパラメータなしコンストラクター
C# 10では、Structのコンストラクターはパラメータなしでも構いません。

```csharp
struct Foo
{
    public int X;
    public Foo() { X = 1; }
}
```

が、`new Foo()`と`default(Foo)`も同じではなくなりました。上記の例には、`new Foo().X`は`1`であり、`default(Foo).X`は`0`です。

## 匿名オブジェクトの`with`
`with`を使用して、新なオブジェクトを作成できます。

```csharp
var x = new { A = 1, B = 2 };
var y = x with { A = 3 }; // y.A は 3 です
```

## グローバル`using`
`global using`を使用すると、プロジェクト全体の使用が可能になり、各ファイルを書き込む必要がなくなります。 たとえば、Import.csを作成して次のように書き込むことができます：

```csharp
global using System;
global using i32 = System.Int32;
```

そして、プロジェクト全体で`using System`は必要がなくなり、`i32`を使用できるようになります。 

## ファイルスコープの名前空間
以前は、名前空間を書き込むために括弧のレイヤーを配置する必要がありました。C# 10ではファイルに名前空間が1つしかない場合は、それを直接先頭に書き込むだけは可能です。

例えば：

```csharp
namespace MyNamespace;
```

## 定数の補間文字列
C# 10では、すべてのプレースホルダー自体が定数文字列の場合、文字列補間を使用して`const`文字列を初期化できます。文字列補間では、アプリケーションで使用される定数文字列を構築するときに、より読みやすい定数文字列を作成できます。

```csharp
const string x = "hello";
const string y = $"{x}, world!";
```

## ラムダの改善
この改善は非常に大きいと言えますので、複数のポイントで紹介します。

### 1. 属性のサポート

```csharp
f = [Foo] (x) => x; // lambdaに配置
f = [return: Foo] (x) => x; // lambdaの戻り値に配置
f = ([Foo] x) => x; // lambdaのパラメーターに配置
```

### 2. 戻り値の型は指定可能


```csharp
f = int () => 4; // intで戻ります
```

### 3. ref、in、outのサポート

```csharp
f = ref int (ref int x) => ref x; // パラメータへの参照を戻ります
```

### 4. ファーストクラス関数
関数は暗黙的にデリゲートに変換できるため、関数はファーストクラスになります。

```csharp
void Foo() { Console.WriteLine("hello"); }
var x = Foo;
x(); // hello
```

### 5. ナチュラルデリゲート
C# 10では、lambdaはデリゲート型を自動的に作成するようになったので、型を書き出す必要はもうありません。

```csharp
var f = () => 1; // Func<int>
var g = string (int x, string y) => $"{y}{x}"; // Func<int, string, string>
var h = "test".GetHashCode; // Func<int>
```

## CallerArgumentExpression
属性`CallerArgumentExpression`がついに実装されました。この属性を使用すると、コンパイラは呼び出しパラメータの式文字列を自動的に入力します。

```csharp
void Foo(int value, [CallerArgumentExpression("value")] string? expression = null)
{
    Console.WriteLine(expression + " = " + value);
}
```

`Foo(4 + 5)`を呼び出すと、`4 + 5 = 9`が出力されます。

## 同じ分解内の代入と宣言
この変更により、以前のバージョンのC#からの制限がなくなります。以前は、分解ですべての値を既存の変数に代入したり、新しく宣言された変数を初期化したりすることができました。

```csharp
int y = 0;
(var x, y, var z) = (1, 2, 3);
```

## インターフェイスは抽象的な静的メソッドのサポート
この機能は、.NET 6でプレビュー機能としてリリースされます。つまり、デフォルトでは有効になっていません。

`<LangVersion>preview</LangVersion>`と`<EnablePreviewFeatures>true</EnablePreviewFeatures>`を設定して、nugetパッケージ`System.Runtime.Experimental`を追加すると、この機能は有効になります。 

そして、インターフェイスは抽象静的メソッドを定義でき、.NET型システムは仮想静的メソッドを遣う機能を備えています。

たとえば、`+`演算子がありゼロを持つインターフェイス`IMonoid<T>`を定義するとします。

```csharp
interface IMonoid<T> where T : IMonoid<T>
{
    abstract static T Zero { get; }
    abstract static T operator+(T l, T r);
}
```

`MyInt`として実装：

```csharp
public class MyInt : IMonoid<MyInt>
{
    public MyInt(int val) { Value = val; }

    public static MyInt Zero { get; } = new MyInt(0);
    public static MyInt operator+(MyInt l, MyInt r) => new MyInt(l.Value + r.Value);

    public int Value { get; }
}
```

次に、`IMoniod<T>`を合計するメソッドの実装：

```csharp
public static class IMonoidExtensions
{
    public static T Sum<T>(this IEnumerable<T> t) where T : IMonoid<T>
    {
        var result = T.Zero;
        foreach (var i in t) result += i;
        return result;
    }
}
```

使用：

```csharp
List<MyInt> list = new() { new(1), new(2), new(3) };
Console.WriteLine(list.Sum().Value); // 6
```

`System.Runtime.Experimental`には.NETの基本型の改善が含まれています。すべての基本型は、数値型の`INumber<T>`の実装など、対応するインターフェイスで実装されます。

インターフェイスの静的抽象メソッドのサポートは、将来C#に追加されるシェイプ機能を補完します。その時点で、C#はインターフェイスとシェイプを使用してHaskellの`class`とRustの`trait`ような機能をサポートし、型システムを大きく改善しますのでご期待を。

## ジェネリック属性
属性はジェネリックをサポートしました。が、この機能は.NET 6でプレビュー機能としてリリースされますので、`<LangVersion>preview</LangVersion>`は必要です。

```csharp
class TestAttribute<T> : Attribute
{
    public T Data { get; }
    public TestAttribute(T data) { Data = data; }
}
```

使用：

```csharp
[Test<int>(3)]
[Test<float>(4.5f)]
[Test<string>("hello")]
```


## メソッドでAsyncMethodBuilder属性の許可

C# 10では、特定のタスクのような型を返すすべてのメソッドに対してメソッドビルダー型を指定するだけでなく、1つのメソッドに対して別の非同期メソッドビルダーを指定することもできます。 これにより、特定のメソッドでカスタムビルダーのベネフィットが得られる高度なパフォーマンスチューニングシナリオが可能になります。

## lineディレクティブの改善
以前は、`#line`はファイル内の行を指定するためにのみ使用できましたが、行、列、および範囲を指定できるようになりました。これは、コンパイラーやコードジェネレーターを作成する人にとって非常に便利です。

```csharp
#line (startLine, startChar) - (endLine, endChar) charOffset "fileName"

// 例：#line (1, 1) - (2, 2) 3 "test.cs"
```

## ネストされた属性パターンマッチングの改善
以前：

```csharp
if (a is { X: { Y: { Z: 4 } } }) { ... }
```

C# 10：

```csharp
if (a is { X.Y.Z: 4 }) { ... }
```

## 改善された文字列補間

以前は、C#の文字列補間はstring.Formatであり、値型パラメーター用に直接ボックス化されていました。これは、パフォーマンスに影響を与えるだけでなく、有用性も限られていました。C# 10では、文字列補間が改善されました：

```csharp
var x = 1;
Console.WriteLine($"hello, {x}");
```

上記のコードは次のようにコンパイルされます：

```csharp
int x = 1;
DefaultInterpolatedStringHandler defaultInterpolatedStringHandler = new DefaultInterpolatedStringHandler(7, 1);
defaultInterpolatedStringHandler.AppendLiteral("hello, ");
defaultInterpolatedStringHandler.AppendFormatted(x);
Console.WriteLine(defaultInterpolatedStringHandler.ToStringAndClear());
```

上記の`DefaultInterpolatedStringHandler`は、`InterpolatedStringHandler`属性を使用して、それを独自の補間ハンドラーに置き換えて、補間の実行方法を決定することもできます。そのため、パフォーマンスも機能も大幅に強化されています。