C#で抽象静的メソッドを使用できないのはなぜですか?


182

私は最近、かなり多くのプロバイダーと連携しており、抽象静的メソッドを持つ抽象クラスが必要な興味深い状況に遭遇しました。このトピックに関するいくつかの投稿を読みましたが、それは理にかなっていますが、わかりやすい説明はありますか?


3
今後の改善のために、これらは開いたままにしてください。
Mark Biek

6
問題は、まさにこのような状況のために、C#には別のキーワードが必要であるという事実に達していると思います。戻り値が呼び出されるタイプにのみ依存するメソッドが必要です。型が不明な場合は、「静的」とは言えません。ただし、型が判明すると、静的になります。「未解決の静的」は考え方です-まだ静的ではありませんが、受け取り側のタイプが分かれば、静的になります。これは完全に優れた概念であり、プログラマーが求め続けるのはこのためです。しかし、それはデザイナーが言語について考える方法に完全に適合しませんでした。
William Jockusch 2015年

@WilliamJockusch受信タイプはどういう意味ですか?BaseClass.StaticMethod()を呼び出すと、BaseClassが、決定を行うために使用できる唯一のタイプになります。ただし、このレベルでは抽象的であるため、メソッドを解決できません。代わりにDerivedClass.StaticMethodをうまく呼び出すと、基本クラスは無関係になります。
Martin Capodici、2016

基本クラスでは、メソッドは未解決であり、使用できません。構造型またはオブジェクト(構造型を持つオブジェクト)が必要です。baseClassObject.Method()またはDerivedClass.Method()を呼び出すことができるはずです。BaseClass.Method()を呼び出すことはできません。型が指定されていないためです。
William Jockusch 2016

回答:


157

静的メソッドはインスタンス化されないため、オブジェクト参照がなくても使用できます。

静的メソッドの呼び出しは、オブジェクト参照ではなくクラス名を介して行われ、それを呼び出す中間言語(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.Testであり、B.Testではなく、A.Testが呼び出されます。

あなたが持っていた場合は、クラスの種類、あなたがタイプしていないオブジェクトを参照する変数を作ることができますデルファイのように、、、あなたは仮想ので、抽象静的メソッド(ともコンストラクタ)のためのより多くの使用を持っているでしょうが、彼らは利用できませんし、したがって、静的呼び出しは.NETでは非仮想です。

ILの設計者は、コードをコンパイルしてB.Testを呼び出し、実行時に呼び出しを解決できることを理解していますが、そこに何らかのクラス名を記述する必要があるため、仮想化はできません。

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

したがって、仮想/抽象静的メソッドは.NETでは使用できません。


4
C#での演算子のオーバーロードの方法と組み合わせると、残念ながら、特定の演算子のオーバーロードの実装を提供するためにサブクラスが必要になる可能性がなくなります。
Chris Moschini、2012年

23
の定義は抽象的Test()AはなくB。で定義されている可能性があるので、この回答はあまり役に立ちません。\

5
ジェネリック型パラメーターは永続的ではない「型」変数として効果的に動作し、仮想静的メソッドはそのような状況で役立つ可能性があります。たとえばCar、仮想静的CreateFromDescriptionファクトリメソッドを持つ型がある場合、Car制約付きのジェネリック型を受け入れるコードは、型の自動車を生成するためにT呼び出すことができます。このような構造は、そのようなメソッドを定義する各型が、仮想「静的」メソッドを保持するネストされたクラスジェネリックの静的シングルトンインスタンスを保持する場合、CLR内でかなりよくサポートされます。T.CreateFromDescriptionT
スーパーキャット2013年

45

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

しばらくの間、静的メソッドを継承できると仮定します。このシナリオを想像してください:

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()を呼び出す場合、どのメソッドが呼び出されますか?どの値が返されましたか?オブジェクトのインスタンスを作成しないと、継承はかなり難しいことが簡単にわかります。継承のない抽象メソッドは、本体を持たないメソッドなので、呼び出すことができません。


33
あなたのシナリオを考えると、Base.GetNumber()は5を返すと思います。Child1.GetNumber()は1を返します。Child2.GetNumber()は2を返します。私があなたの推論を理解するのを助けるために、私が間違っていることを証明できますか?ありがとう
Luis Filipe

Base.GetNumber()が5を返すと思う顔は、何が起こっているのかすでに理解していることを意味します。ベース値を返すことにより、継承は行われません。
David Wengier、2008年

60
なぜ世界でBase.GetNumber()が5以外のものを返すのですか?これは基本クラスのメソッドです-オプションは1つだけです。
Artem Russakovskii 2009年

4
@ArtemRussakovskii:あるとしましょうint DoSomething<T>() where T:Base {return T.GetNumber();}DoSomething<Base>()5 DoSomething<Child2>()を返し、2 を返すと便利です。このような機能は、おもちゃの例だけでなく、のようなものにも役立ちます。この場合class Car {public static virtual Car Build(PurchaseOrder PO);}、から派生するすべてのクラスCarは、注文書を指定してインスタンスを作成できるメソッドを定義する必要があります。
スーパーキャット2013年

4
静的でない継承とまったく同じ「問題」があります。
アークくん

18

別の回答者(McDowell)は、ポリモーフィズムはオブジェクトインスタンスに対してのみ機能すると述べています。修飾する必要があります。クラスを「クラス」または「メタクラス」タイプのインスタンスとして扱う言語があります。これらの言語は、インスタンスメソッドとクラス(静的)メソッドの両方でポリモーフィズムをサポートしています。

C#は、その前のJavaやC ++のように、そのような言語ではありません。staticキーワードは、メソッドが静的結合ではなく動的/仮想されていることを示すために明示的に使用されます。


9

静的フィールドとメソッドの継承が確実に必要な状況は次のとおりです。

abstract class Animal
{
  protected static string[] legs;

  static Animal() {
    legs=new string[0];
  }

  public static void printLegs()
  {
    foreach (string leg in legs) {
      print(leg);
    }
  }
}


class Human: Animal
{
  static Human() {
    legs=new string[] {"left leg", "right leg"};
  }
}


class Dog: Animal
{
  static Dog() {
    legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"};
  }
}


public static void main() {
  Dog.printLegs();
  Human.printLegs();
}


//what is the output?
//does each subclass get its own copy of the array "legs"?

4
いいえ、配列「legs」のインスタンスは1つしかありません。静的コンストラクターが呼び出される順序がわからないため、出力は非決定的です(実際には、基本クラスの静的コンストラクターが呼び出される保証はまったくありません)。「ニード」はかなり絶対的な用語であり、「欲望」はおそらくより正確です。
Sam

legs静的抽象プロパティである必要があります。
リトルエンディアン2016

8

前の説明に加えて、静的メソッド呼び出しはcompile-timeで特定のメソッドにバインドされます。これにより、ポリモーフィックな動作が除外されます。


C#は静的に型指定されています。ポリモーフィックメソッドの呼び出しも、私が理解しているようにコンパイル時にバインドされます。つまり、実行時に呼び出すメソッドを解決するためにCLRが残されていません。
Adam Tolley、

では、ポリモーフィズムはCLRでどのように機能すると思いますか?あなたの説明は仮想メソッドのディスパッチを除外しました。
リトミス、

それは実際のところ、それほど有用なコメントではありません。私は( '私が理解しているように')有用な談話を招待しました。おそらく、もう少しコンテンツを提供できると思います。しかし、私は同じことを犯しているようです-私は実際に上のコメントを質問として意味しました:コンパイル時にC#はこれらのものを評価しませんか?
Adam Tolley、

申し訳ありませんが、私は侮辱を意味するものではありませんでした(私は少し急いで応答することは認めますが;-)。私の質問の要点は、これらのクラスがある場合は次のとおりです。}クラスDerived:Base {public override void Method(); このように記述します:Base instance = new Derived(); instance.Method(); 呼び出しサイトのコンパイル時の型情報は、実際のインスタンスがDerivedである場合、Baseのインスタンスを取得したことです。そのため、コンパイラは呼び出すメソッドを正確に解決できません。代わりに、それは...派遣へのランタイムを告げる「callvirt」IL命令を発する
Rytmis

1
ありがとう、それは有益です!私がILに飛び込むのを十分に延期していると思います。幸運を祈ります。
アダムトーリー

5

(delphiの)静的メソッドを実際にオーバーライドしますが、少し見苦しいですが、私たちのニーズには問題なく動作します。

これを使用して、クラスがクラスインスタンスなしで使用可能なオブジェクトのリストを持つことができるようにします。たとえば、次のようなメソッドがあります。

class function AvailableObjects: string; override;
begin
  Result := 'Object1, Object2';
end; 

醜いですが必要です。これにより、使用可能なオブジェクトを検索するためだけにすべてのクラスをインスタンス化する代わりに、必要なものだけをインスタンス化できます。

これは簡単な例ですが、アプリケーション自体は、1つのサーバーですべてのクラスを使用できるクライアントサーバーアプリケーションであり、サーバーが持つすべてのものを必要とせず、オブジェクトインスタンスを必要としない複数の異なるクライアントです。

そのため、クライアントごとに1つの異なるサーバーアプリケーションを用意するよりも、保守がはるかに簡単です。

例が明確であることを願っています。


0

抽象メソッドは暗黙的に仮想です。抽象メソッドにはインスタンスが必要ですが、静的メソッドにはインスタンスがありません。したがって、抽象クラスに静的メソッドを含めることができますが、静的抽象(または抽象静的)にすることはできません。


1
-1仮想メソッドは、設計による場合を除き、インスタンスを必要としません。そして、あなたは実際に問題に取り組むのではなく、それをそらすだけです。
FallenAvatar 2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.