私は最近、かなりの量のプロバイダーを扱ってきましたが、抽象静的メソッドを持つ抽象クラスが必要な興味深い状況に遭遇しました。このトピックに関するいくつかの投稿を読みましたが、それはある程度理にかなっていますが、明確な説明はありますか?
10 に答える
静的メソッドはインスタンス化されず、オブジェクト参照なしで利用できます。
静的メソッドの呼び出しは、オブジェクト参照ではなくクラス名を介して行われ、それを呼び出すための中間言語 (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 に対して行われます。これは、それを定義したのは A クラスであり、B.Test に対してではありません。ただし、そのようにコードを記述することはできます。
Delphi のように、オブジェクトではなく型を参照する変数を作成できるクラス型がある場合、仮想および抽象静的メソッド (およびコンストラクター) をより多く使用できますが、それらは利用できず、したがって、静的呼び出しは .NET では非仮想です。
IL 設計者は、コードをコンパイルして B.Test を呼び出し、実行時に呼び出しを解決できることを認識していますが、何らかのクラス名をそこに記述する必要があるため、まだ仮想ではありません。
仮想メソッド、つまり抽象メソッドは、実行時にさまざまな種類のオブジェクトを含むことができる変数を使用しているため、変数にある現在のオブジェクトに対して適切なメソッドを呼び出したい場合にのみ役立ちます。静的メソッドでは、とにかくクラス名を調べる必要があるため、呼び出す正確なメソッドはコンパイル時にわかります。変更できないためです。
したがって、仮想/抽象静的メソッドは .NET では使用できません。
静的メソッドは継承またはオーバーライドできないため、抽象化できません。静的メソッドはクラスのインスタンスではなく型で定義されるため、その型で明示的に呼び出す必要があります。したがって、子クラスでメソッドを呼び出したい場合は、その名前を使用して呼び出す必要があります。これにより、継承は無関係になります。
少しの間、静的メソッドを継承できると仮定します。次のシナリオを想像してください。
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() を呼び出すと、どのメソッドが呼び出されますか? どの値が返されましたか? オブジェクトのインスタンスを作成しないと、継承がかなり難しいことが簡単にわかります。継承のない抽象メソッドは、本体を持たないメソッドであるため、呼び出すことができません。
別の回答者(McDowell)は、ポリモーフィズムはオブジェクトインスタンスに対してのみ機能すると述べました。それは修飾されるべきです。クラスを「クラス」または「メタクラス」タイプのインスタンスとして扱う言語があります。これらの言語は、インスタンスメソッドとクラス(静的)メソッドの両方でポリモーフィズムをサポートしています。
C#は、それ以前のJavaやC ++と同様に、そのような言語ではありません。キーワードは、static
メソッドが動的/仮想ではなく静的にバインドされていることを示すために明示的に使用されます。
以下は、静的フィールドとメソッドの継承が確実に必要な状況です。
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"?
前の説明に加えて、静的メソッドの呼び出しはコンパイル時に特定のメソッドにバインドされます。これにより、ポリモーフィックな動作が除外されます。
実際には静的メソッドを (Delphi で) オーバーライドします。これは少し醜いですが、私たちのニーズには問題なく機能します。
これを使用して、クラスがクラス インスタンスなしで使用可能なオブジェクトのリストを持つことができるようにします。たとえば、次のようなメソッドがあります。
class function AvailableObjects: string; override;
begin
Result := 'Object1, Object2';
end;
これは醜いですが必要です。このようにして、利用可能なオブジェクトを検索するためだけにすべてのクラスをインスタンス化するのではなく、必要なものだけをインスタンス化できます。
これは単純な例でしたが、アプリケーション自体は、1 つのサーバーですべてのクラスを使用できるクライアント サーバー アプリケーションであり、複数の異なるクライアントは、サーバーが持つすべてのものを必要とせず、オブジェクト インスタンスを必要としません。
そのため、クライアントごとに 1 つの異なるサーバー アプリケーションを用意するよりも、保守がはるかに簡単です。
例が明確であることを願っています。
抽象メソッドは暗黙的に仮想です。抽象メソッドにはインスタンスが必要ですが、静的メソッドにはインスタンスがありません。したがって、抽象クラスに静的メソッドを含めることはできますが、静的抽象 (または抽象静的) にすることはできません。
現在、プレビュー機能として C# 10 で利用できます。