rksoftware

Visual Studio とか C# とかが好きです

C# 11 の新機能を確認「 ジェネリック型数値演算のサポート - インターフェイスの static virtual メンバー 」

C# 11 の新機能を確認しています。目次は次の記事です。
rksoftware.hatenablog.com

今回は 「 ジェネリック型数値演算のサポート - インターフェイスの static virtual メンバー 」。公式 Learn の記事は次です。

learn.microsoft.com

インターフェイスに static virtual メソッド、static abstract メソッドを定義できます。

■ 確認

次のようなコードが書けます。

interface IInterface
{
    static int Method1(int arg) => default;
    static virtual int Method2(int arg) => default;
    static abstract int Method3(int arg);
}

ちなみにこのコードは .NET 6 を指定した場合は次のエラーになります。

エラー CS8703 C# 10.0 では、修飾子 'virtual' はこの項目に対して有効ではありません。'11.0' 以上の言語バージョンをご使用ください。
エラー CS8703 C# 10.0 では、修飾子 'abstract' はこの項目に対して有効ではありません。'11.0' 以上の言語バージョンをご使用ください。
interface IInterface
{
    static int Method1(int arg) => default;
    static virtual int Method2(int arg) => default; // エラー CS8703 C# 10.0 では、修飾子 'virtual' はこの項目に対して有効ではありません。'11.0' 以上の言語バージョンをご使用ください。
    static abstract int Method3(int arg);           // エラー CS8703 C# 10.0 では、修飾子 'abstract' はこの項目に対して有効ではありません。'11.0' 以上の言語バージョンをご使用ください。
}

これらのメソッドを直接使おうとするとエラーになります。

IInterface.Method1(0);
IInterface.Method2(0);  // エラー CS8926 静的な仮想または抽象インターフェイス メンバーには、型パラメーターでのみアクセスできます。
IInterface.Method3(0);  // エラー CS8926 静的な仮想または抽象インターフェイス メンバーには、型パラメーターでのみアクセスできます。

interface IInterface
{
    static int Method1(int arg) => default;
    static virtual int Method2(int arg) => default;
    static abstract int Method3(int arg);
}

インターフェイスを実装する際、static abstract メソッドは実装する必要がありますが、static virtual メソッドは実装しなくても OK です。ただし、実装していないと呼び出せません。

IInterface.Method1(0);
CClass.Method2(0); // エラー CS0117 'CClass' に 'Method2' の定義がありません
CClass.Method3(0);

interface IInterface
{
    static int Method1(int arg) => default;
    static virtual int Method2(int arg) => default;
    static abstract int Method3(int arg);
}

class CClass : IInterface
{
    public static int Method3(int arg) => default;
}

static virtual メソッドも実装すれば呼び出せます。

IInterface.Method1(0);
CClass.Method2(0); // OK
CClass.Method3(0);

interface IInterface
{
    static int Method1(int arg) => default;
    static virtual int Method2(int arg) => default;
    static abstract int Method3(int arg);
}

class CClass : IInterface
{
    public static int Method2(int arg) => default;  // これがあると呼び出せる
    public static int Method3(int arg) => default;
}

■ ジェネリック インターフェイス

この機能はジェネリック インターフェイスで使うことを想定しているようです。こんな感じになるのですかね。

CClass.Method2(new CClass());
CClass.Method3(new CClass());

interface IInterface<TSelf> where TSelf: IInterface<TSelf>
{
    static virtual TSelf Method2(TSelf arg) => default;
    static abstract TSelf Method3(TSelf arg);
}

class CClass : IInterface<CClass>
{
    public static CClass Method2(CClass arg) => default;
    public static CClass Method3(CClass arg) => default;
}

演算子のオーバーロード

演算子のオーバーロードに使うことも想定しているようですね。C# では演算子のオーバーロードはあまりしない人が多いと思いますが、次のコードのような感じです。

var value = new CClass(1) + new CClass(2);
Console.WriteLine(value);   // CClass { Value = 3 }

interface IInterface<TSelf> where TSelf : IInterface<TSelf>
{
    static abstract TSelf operator +(TSelf arg1, TSelf arg2);
}

record struct CClass(int Value) : IInterface<CClass>
{
    public static CClass operator +(CClass arg1, CClass arg2) => new(arg1.Value + arg2.Value);
}

■ 覚えておきましょう

この機能は使わない人はほとんど使わない機能かもしれません。しかしライブラリが提供する型では使われることもあるでしょう。書けなくても覚えておかないと素晴らしいライブラリなど使いこなせなくなる日が来ると思います。覚えておきましょう。