web-dev-qa-db-ja.com

C#静的メンバー「継承」-なぜこれがまったく存在するのですか?

C#では、スーパークラスの静的メンバーがサブクラスのスコープに「継承」されます。例えば:

_class A { public static int M() { return 1; } }
class B : A {}
class C : A { public new static int M() { return 2; } }
[...]
A.M(); //returns 1
B.M(); //returns 1 - this is equivalent to A.M()
C.M(); //returns 2 - this is not equivalent to A.M()
_

今、あなたは静的クラスを継承することはできません、そして静的継承が問題になるかもしれないと私が想像できる唯一の場所はそれを完全に無視します:Tのサブクラスである型パラメーターAを必要とする一般的な制約を作成することはできますが、まだ呼び出すことはできませんT.M()(おそらくVMの機能を簡素化します)。サブクラスに別のM実装を記述してそれを使用することは言うまでもありません。

したがって、静的メンバーの「継承」は、単に名前空間の汚染のように見えます。名前を明示的に修飾した場合でも(つまり、_B.M_)、Aのバージョンは解決されます。

Edit名前空間と比較:

_namespace N1{  class X();   }
namespace N1.N2 {  class X();   }
namespace N1.N2.N3 { [...] }
_

_N1.N2.N3_内修飾なしでXを使用する場合、_N1.N2.X_を参照することは理にかなっています。しかし、明示的に_N1.N2.N3.X_を参照し、そのようなクラスが存在しない場合、_N2_のバージョンが見つかるとは思わない。実際、これを実行しようとすると、コンパイラがエラーを報告します。対照的に、明示的にB.M()を参照した場合、コンパイラがエラーを報告しないのはなぜですか?結局のところ、「B」には「M」メソッドはありません...

この継承にはどのような目的がありますか?この機能を何らかの方法で建設的に使用できますか?

46
Eamon Nerbonne

したがって、静的メンバーの「継承」は名前空間汚染のように見えるだけです

その通りですが、ある男の汚染が別の男のスパイシーな味付けである点が異なります。

Martin FowlerがDSLに関する彼の研究で、静的メソッドへの便利なアクセスを可能にするためにこの方法で継承を使用することを提案し、クラス名の修飾なしでそれらのメソッドを使用できるようにしたと思います。そのため、呼び出しコードは、メソッドが定義されているクラスを継承するクラス内にある必要があります。 (私はそれは腐った考えだと思います。)

私の意見では、静的メンバーを非静的目的のクラスに混在させることはできません。ここで提起する問題は、それらを混在させないことが重要である理由の一部です。

静的なプライベートmutableデータを非表示にすると、それ以外の場合は「インスタンス化」されたクラスの実装内が特に恐ろしくなります。ただし、静的メソッドがあり、これはさらに悪いミキサーです。以下は、クラスに混合された静的メソッドの一般的な使用方法です。

public class Thing
{
    // typical per-instance stuff
    int _member1;
    protected virtual void Foo() { ... }
    public void Bar() { ... }

    // factory method
    public static Thing Make()
    {
        return new Thing();
    }
}

これは静的なファクトリメソッドパターンです。それはほとんどの場合無意味ですが、さらに悪いことに、今これがありました:

public class AnotherThing : Thing { }

これには、MakeではなくThingを返す静的AnotherThingメソッドがあります。

この種の不一致は、静的メソッドを持つものはすべてシールする必要があることを強く意味します。静的メンバーは継承とうまく統合できません。それらを遺伝性にすることは意味がありません。そのため、静的なものを別々の静的クラスに保持し、クラスが静的であると既に述べたときに、すべてのメンバーを静的に宣言する必要があることを冗長にしています。

しかし、それはこれらの遅すぎるものの1つにすぎません。すべての実際の実用的な言語(およびライブラリと製品)には、いくつかの言語があります。 C#にはほとんどありません。

25

むしろ、派生クラスのすべてのベースの静的メンバーにアクセスできます。それ以外の場合は、静的メンバーが定義されている場所を正確に把握し、明示的に呼び出す必要があります。

Intellisenseを使用すると、その種類のクラスで使用できるすべての静的メンバーを自動的に知ることができます。

もちろん、それらは継承されません、それは単なるショートカットです

10
Luis Filipe

それがどのように機能するか、ほとんどの場合、おそらく愚かな答えになるでしょう。しかし、この場合、それがどのように機能するかです。 Aから派生したので、あなたはA +追加した追加機能であると言います。

したがって、Aのインスタンスを介してアクセスするのと同じ変数にアクセスできる必要があります。

ただし、静的クラスを継承しても意味がありませんが、静的メンバー/フィールド/メソッドにはアクセスできます。

この例は次のとおりです。

internal class BaseUser
{
    public static string DefaultUserPool { get; set; }
}
internal class User : BaseUser
{
    public int Id { get; set; }
    public string Name { get; set; }
    public User Parent { get; set; }
}

テストは次のようになります。

User.DefaultUserPool = "Test";
BaseUser.DefaultUserPool = "Second Test";

Console.WriteLine(User.DefaultUserPool);
Console.WriteLine(BaseUser.DefaultUserPool);

WriteLinesはどちらも「2番目のテスト」を出力します。これは、BaseUserとUserの両方がDefaultUserPoolを使用する必要があるためです設計による。また、静的に実装されたメソッドをオーバーライドしても、それは子クラスの単なるアクセサーであるため、mucnには意味がありません。

一つだけ存在することができます。オーバーライドすることは、そのサブクラスの新しい実装が存在することを意味し、「静的」という用語を無効にします。

8
Filip Ekberg

実際、私が理解しているように、これはコンパイラが提供するショートカットにすぎません。構文糖。 BにはB.M()がなく、Aにもあるため、A.M()static M()にコンパイルされます。書くのが簡単になるためです。 「静的継承」はありません。

追加:そして、「再定義」するときのnewの要件は、誤って足を撃ち抜かないようにするためです。

5
Vilx-

基本クラスの保護された静的メンバーにアクセスするためだと思います。

class Base
{
    protected static void Helper(string s)
    {
       Console.WriteLine(s);
    }
}

class Subclass : Base
{
   public void Run()
    {
       Helper("From the subclass");
    }
}
0
TrueWill

だから...代替は何ですか?

質問は言及しています...

コンパイラがエラーを報告しないのはなぜですか?結局のところ、「B」には「M」メソッドはありません...

しかし、is派生 "M"メソッドが "B"にありますクラス。

コンパイラーがプログラマーに基本ケースの統合仮想テーブルを提示しなかった場合、プログラマーは静的メソッドを見つけるために基本型を探し回らなければなりません。これは polymorphism を壊します。

ウィキペディア...

サブタイプポリモーフィズムは、ほとんどの場合オブジェクト指向プログラミングのコンテキストで単にポリモーフィズムと呼ばれ、あるタイプAが別のタイプBのように表示されて使用される能力です。

強く型付けされた言語では、ポリモーフィズムは通常、型Aが型Bから何らかの形で派生するか、または型Cが型Bを表すインターフェースを実装することを意味します。

0
kervin

すべての子クラスで同じ機能を保持したいアイテムの継承クラスによって、あらゆる形態のポリモーフィズムを防ぐ手段であると私はいつも思っています。

上記のことを何らかの理由で無視します。静的ではなくシールを考えていました

静的なメンバー変数と関数を使用して、データまたはfunctionallityが一度だけインスタンス化されるため、クラスインスタンスに依存しないようにすると思います。

使用例としては、スーパークラスのサブクラスのすべてのインスタンスのライブカウントを維持するカウンター値があります(各サブクラスは、構築時に静的カウント値を増分します)。このカウント値は、サブクラスのすべてのインスタンスで利用可能であり、等しくなります。

0
ChrisBD