C#では、なぜ抽象的な静的メソッドを持つことができないのですか?

最近、プロバイダをかなり使っているのですが、抽象クラスが抽象スタティックメソッドを持つという面白い状況に出くわしたんです。このトピックに関するいくつかの投稿を読んで、なんとなく納得したのですが、明確な説明はあるのでしょうか?

ソリューション

静的メソッドは、オブジェクトの参照なしに利用できるもので、そのように インスタンス化 されていません。

静的メソッドの呼び出しは、オブジェクトの参照ではなくクラス名を通して行われ、呼び出すための中間言語(IL)コードは、必ずしも使用したクラス名ではなく、その抽象メソッドを定義したクラス名を通して呼び出します。

例を示そう。

次のようなコードで

public class A
{
    public static void Test()
    {
    }
}

public class B : A
{
}

こんな感じで、B.Testを呼び出すと。

class Program
{
    static void Main(string[] args)
    {
        B.Test();
    }
}

そうすると、実際のMainメソッド内のコードは以下のようになります。

.entrypoint
.maxstack 8
L0000: nop 
L0001: call void ConsoleApplication1.A::Test()
L0006: nop 
L0007: ret 

ご覧のように、そのようにコードを書くことができても、定義したのがAクラスであるため、A.Testを呼び出すのであって、B.Testを呼び出すのではありません。

Delphiのように、オブジェクトではなく型を参照する変数を作ることができるクラス型があれば、仮想的な、つまり抽象的な静的メソッド(およびコンストラクタも)をもっと活用できるはずですが、これらは利用できず、したがって静的呼び出しも.NETでは非仮想的なものとなっています。

IL設計者は、B.Testを呼び出すようにコードをコンパイルし、実行時に呼び出しを解決することを許可することはできますが、それでもそこに何らかのクラス名を書かなければならないので、仮想にはなりません'。

仮想メソッド、つまり抽象メソッドは、実行時にさまざまな種類のオブジェクトを含むことができる変数を使用する場合にのみ有効であり、したがって、変数内にある現在のオブジェクトに適したメソッドを呼び出すことができます。静的メソッドでは、とにかくクラス名を調べる必要があり、コンパイル時に呼び出すべき正確なメソッドを知ることができます。

このように、.NETでは仮想/抽象の静的メソッドは使えません。

解説 (3)

静的メソッドは継承やオーバーライドができないので、抽象的なメソッドにはなりえません。静的メソッドはクラスのインスタンスではなく型に対して定義されるので、その型に対して明示的に呼び出さなければなりません。つまり、子クラスのメソッドを呼び出したい場合は、そのクラスの名前を使って呼び出す必要があるのです。これでは、継承は無意味です。

仮に、静的メソッドを継承することができたとします。こんなシナリオを想像してみてください。

public static class Base
{
    public static virtual int GetNumber() { return 5; }
}

public static class Child1 : Base
{
    public static override int GetNumber() { return 1; }
}

public static class Child2 : Base
{
    public static override int GetNumber() { return 2; }
}

Base.GetNumber()を呼び出した場合、どのメソッドが呼び出されるでしょうか?どの値が返されますか?このように、オブジェクトのインスタンスを生成しなければ、継承は難しいということがよくわかります。継承のない抽象メソッドは、ボディを持たないメソッドなので、呼び出すことができません。

解説 (5)

これまでの説明を補足すると、静的メソッド呼び出しはコンパイル時に特定のメソッドに束縛されるため、むしろポリモーフィックな振る舞いを排除している。

解説 (6)