web-dev-qa-db-ja.com

なぜ静的クラスを継承できないのですか?

本当に状態を必要としないクラスがいくつかあります。組織の観点から、私はそれらを階層に入れたいと思います。

しかし、静的クラスの継承を宣言できないようです。

そんな感じ:

public static class Base
{
}

public static class Inherited : Base
{
}

動作しないでしょう。

なぜ言語のデザイナーはその可能性を閉じたのですか?

212
User

here からの引用:

これは実際には設計によるものです。静的クラスを継承する正当な理由はないようです。クラス名自体を介していつでもアクセスできるパブリック静的メンバーがあります。 静的なものを継承するために私が見た唯一の理由は、タイピングの数文字を保存するなどの悪いものでした。

静的メンバーを直接スコープに入れるメカニズムを検討する理由があるかもしれません(そして、Orcas製品サイクルの後にこれを実際に検討します)が、静的クラスの継承は進むべき方法ではありません。たまたま静的クラスに存在する静的メンバーのみ。

(Mads Torgersen、C#言語PM)

channel9 からの他の意見

。NETの継承は、インスタンスベースでのみ機能します。静的メソッドは、インスタンスレベルではなく、タイプレベルで定義されます。静的メソッド/プロパティ/イベントでオーバーライドが機能しないのはそのためです...

静的メソッドはメモリに一度だけ保持されます。それらのために作成される仮想テーブルなどはありません。

.NETでインスタンスメソッドを呼び出す場合、常に現在のインスタンスを指定します。これは.NETランタイムによって隠されますが、起こります。 各インスタンスメソッドは、最初の引数として、メソッドが実行されるオブジェクトへのポインター(参照)を持ちます。これは、静的メソッドでは発生しません(型レベルで定義されているため)。コンパイラは、呼び出すメソッドを選択する方法をどのように決定する必要がありますか?

(littleguru)

貴重なアイデアとして、littleguruにはこの問題に対する部分的な「回避策」があります: Singleton パターン。

167
boj

静的クラスを継承できない主な理由は、それらが抽象クラスであり、シールされていることです(これにより、インスタンスが作成されなくなります)。

したがって、この:

static class Foo { }

このILにコンパイルします。

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }
70
Andrew Hare

このように考えてください。次のように、型名を介して静的メンバーにアクセスします。

MyStaticType.MyStaticMember();

そのクラスから継承する場合、新しい型名を介してアクセスする必要があります。

MyNewType.MyStaticMember();

したがって、コードで使用する場合、新しいアイテムは元のアイテムとは関係がありません。ポリモーフィズムなどの継承関係を利用する方法はありません。

おそらく、元のクラスのアイテムの一部を拡張したいだけだと考えているのでしょう。その場合、元のメンバーをまったく新しい型で使用することを妨げるものは何もありません。

おそらく、既存の静的型にメソッドを追加したいでしょう。あなたはすでに拡張メソッドを介してそれを行うことができます。

おそらく、実行時に静的なTypeを関数に渡し、メソッドの実行内容を正確に知らなくても、そのタイプのメソッドを呼び出すことができます。その場合、インターフェイスを使用できます。

したがって、最終的には、静的クラスを継承しても実際には何も得られません。

23
Joel Coehoorn

クラス階層を使用して達成したいことは、名前空間を通してのみ達成できます。したがって、namespapces(C#など)をサポートする言語では、静的クラスのクラス階層の実装は使用されません。どのクラスもインスタンス化できないため、必要なのは、名前空間を使用して取得できるクラス定義の階層構造です

3
Amit

代わりにコンポジションを使用できます...これにより、静的型からクラスオブジェクトにアクセスできます。しかし、まだインターフェイスまたは抽象クラスを実装することはできません

3
Yogesh Karekar

継承されたクラス名を通じて「継承された」静的メンバーにアクセスできますが、静的メンバーは実際には継承されません。これは、一部が仮想または抽象的であり、オーバーライドできない理由の一部です。あなたの例では、Base.Method()を宣言した場合、コンパイラはInherited.Method()の呼び出しをとにかくBase.Method()にマップし直します。 Base.Method()を明示的に呼び出すこともできます。小さなテストを作成して、Reflectorで結果を確認できます。

静的メンバーを継承できない場合、および静的クラスにonly静的メンバーを含めることができる場合、静的クラスを継承すると何が良いでしょうか?

2
Lucas

うーん...静的メソッドで満たされた非静的クラスがあった場合、それは大きく異なりますか?

1
CookieOfFortune

回避策は、静的クラスを使用せず、コンストラクタを非表示にして、クラスの静的メンバーのみがクラスの外部からアクセスできるようにすることです。結果は、本質的に継承可能な「静的」クラスです。

public class TestClass<T>
{
    protected TestClass()
    { }

    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public class TestClass : TestClass<double>
{
    // Inherited classes will also need to have protected constructors to prevent people from creating instances of them.
    protected TestClass()
    { }
}

TestClass.Add(3.0, 4.0)
TestClass<int>.Add(3, 4)

// Creating a class instance is not allowed because the constructors are inaccessible.
// new TestClass();
// new TestClass<int>();

残念ながら、「設計による」言語の制限のため、私たちはできません。

public static class TestClass<T>
{
    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public static class TestClass : TestClass<double>
{
}
1
FocusedWolf

静的継承のように見える何かをすることができます。

ここにトリックがあります:

public abstract class StaticBase<TSuccessor>
    where TSuccessor : StaticBase<TSuccessor>, new()
{
    protected static readonly TSuccessor Instance = new TSuccessor();
}

次に、これを行うことができます:

public class Base : StaticBase<Base>
{
    public Base()
    {
    }

    public void MethodA()
    {
    }
}

public class Inherited : Base
{
    private Inherited()
    {
    }

    public new static void MethodA()
    {
        Instance.MethodA();
    }
}

Inheritedクラス自体は静的ではありませんが、作成することはできません。実際には、Baseを構築する静的コンストラクターを継承しており、Baseのすべてのプロパティとメソッドは静的として使用できます。静的コンテキストに公開する必要がある各メソッドとプロパティの静的ラッパーを作成するのは、あと1つだけです。

静的ラッパーメソッドとnewキーワードを手動で作成する必要があるなどの欠点があります。しかし、このアプローチは、静的継承に本当に似ているものをサポートするのに役立ちます。

追伸これをコンパイル済みクエリの作成に使用しました。実際にはConcurrentDictionaryに置き換えることができますが、スレッドセーフの静的な読み取り専用フィールドで十分でした。

1
Konard

私の答え:デザインの選択が悪い。 ;-)

これは、構文の影響に焦点を当てた興味深い議論です。私の見解では、議論の核心は、設計上の決定が静的クラスを封印することにつながったということです。子の名前の後ろに隠れている(「混乱している」)のではなく、最上位に表示される静的クラスの名前の透明性に焦点を当てていますか?ベースまたは子に直接アクセスできる言語実装をイメージできますが、混乱します。

静的継承が何らかの方法で定義されていると仮定した擬似的な例。

public static class MyStaticBase
{
    SomeType AttributeBase;
}

public static class MyStaticChild : MyStaticBase
{
    SomeType AttributeChild;
}

につながるだろう:

 // ...
 DoSomethingTo(MyStaticBase.AttributeBase);
// ...

どのストレージと同じストレージに影響を与える可能性がありますか?

// ...
DoSomethingTo(MyStaticChild.AttributeBase);
// ...

非常に紛らわしいです!

ちょっと待って!コンパイラは、両方で同じシグネチャが定義されているMyStaticBaseとMyStaticChildをどのように処理しますか?上の例よりも子がオーバーライドしても同じストレージが変更されない場合は、おそらく?これはさらに混乱を招きます。

私は、限られた静的継承のための強力な情報空間の正当化があると信じています。制限の詳細については後ほど説明します。この擬似コードは値を示します:

public static class MyStaticBase<T>
{
   public static T Payload;
   public static void Load(StorageSpecs);
   public static void Save(StorageSpecs);
   public static SomeType AttributeBase
   public static SomeType MethodBase(){/*...*/};
}

次に、あなたが得る:

public static class MyStaticChild : MyStaticBase<MyChildPlayloadType>
{
   public static SomeType AttributeChild;
   public static SomeType SomeChildMethod(){/*...*/};
   // No need to create the PlayLoad, Load(), and Save().
   // You, 'should' be prevented from creating them, more on this in a sec...
} 

使用法は次のようになります。

// ...
MyStaticChild.Load(FileNamePath);
MyStaticChild.Save(FileNamePath);
doSomeThing(MyStaticChild.Payload.Attribute);
doSomething(MyStaticChild.AttributeBase);
doSomeThing(MyStaticChild.AttributeChild);
// ...

静的な子を作成する人は、プラットフォームまたは環境のシリアル化エンジンに課せられる制限を理解している限り、シリアル化プロセスについて考える必要はありません。

静的(シングルトンおよび他の形式の「グローバル」)は、多くの場合、構成ストレージの周りに現れます。静的継承により、この種の責任の割り当てを構文で明確に表現して、構成の階層に一致させることができます。ただし、私が示したように、基本的な静的継承の概念が実装されている場合、大きなあいまいさが生じる可能性が十分にあります。

適切な設計上の選択は、特定の制限付きで静的継承を許可することだと思います。

  1. 何もオーバーライドしません。子はベース属性、フィールド、またはメソッドを置き換えることはできません...コンパイラーが子とベースを区別できるシグネチャに違いがある限り、オーバーロードは問題ありません。
  2. 汎用静的ベースのみを許可し、非汎用静的ベースから継承することはできません。

汎用参照MyStaticBase<ChildPayload>.SomeBaseFieldを介して同じストアを変更することもできます。ただし、ジェネリック型を指定する必要があるため、落胆するでしょう。一方、子参照はよりクリーンになります:MyStaticChild.SomeBaseField

私はコンパイラの作成者ではないので、これらの制限をコンパイラに実装することの難しさについて何か見逃しているかどうかはわかりません。そうは言っても、限られた静的継承には情報スペースが必要であり、基本的な答えは、貧弱な(または単純すぎる)デザインの選択のためにできないという強い信念です。

0
Dale Strickler

静的クラスとクラスメンバーは、クラスのインスタンスを作成せずにアクセスできるデータと関数を作成するために使用されます。静的クラスメンバを使用して、オブジェクトのIDに依存しないデータと動作を分離できます。データと関数は、オブジェクトに何が起こっても変更されません。静的クラスは、オブジェクトIDに依存するクラスにデータまたは動作がない場合に使用できます。

クラスは静的と宣言できます。これは、静的メンバーのみが含まれていることを示します。新しいキーワードを使用して静的クラスのインスタンスを作成することはできません。静的クラスは、クラスを含むプログラムまたは名前空間が読み込まれると、.NET Framework共通言語ランタイム(CLR)によって自動的に読み込まれます。

静的クラスを使用して、特定のオブジェクトに関連付けられていないメソッドを含めます。たとえば、インスタンスデータに作用せず、コード内の特定のオブジェクトに関連付けられていない一連のメソッドを作成することは一般的な要件です。静的クラスを使用してこれらのメソッドを保持できます。

静的クラスの主な機能は次のとおりです。

  1. 静的メンバーのみが含まれます。

  2. それらはインスタンス化できません。

  3. 彼らは封印されています。

  4. インスタンスコンストラクターを含めることはできません(C#プログラミングガイド)。

したがって、静的クラスの作成は、基本的に、静的メンバーとプライベートコンストラクターのみを含むクラスの作成と同じです。プライベートコンストラクターは、クラスのインスタンス化を防ぎます。

静的クラスを使用する利点は、コンパイラがインスタンスメンバが誤って追加されていないことを確認できることです。コンパイラは、このクラスのインスタンスを作成できないことを保証します。

静的クラスは封印されているため、継承できません。 Object以外のクラスから継承することはできません。静的クラスにインスタンスコンストラクターを含めることはできません。ただし、静的コンストラクタを持つことができます。詳細については、「静的コンストラクター(C#プログラミングガイド)」を参照してください。

0
Zone