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


224

本当に状態を必要としないクラスがいくつかあります。組織的には階層化したい。

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

そんな感じ:

public static class Base
{
}

public static class Inherited : Base
{
}

動作しないでしょう。

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


回答:


174

ここからの引用:

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

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

(Mads Torgersen、C#言語PM)

チャンネル9からの他の意見

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

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

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

(リトルグル)

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


92
Torgersen側の想像力の欠如、本当に。私はこれをしたいと思ったかなり良い理由がありました:)
Thorarin

10
これはどう?オープンソースではないDLLがあります/そのソースにアクセスできません(NUnitなど)。そのAssertクラスを拡張して、Throws <>(Action a)のようなメソッドを持たせたいとします(そうですが、ある時点ではありませんでした)。プロジェクトにAssert:NUnit.Framework.Assertを追加できます。クラスを作成し、Throwsメソッドを追加します。問題が解決しました。コードで使用する方が簡単です...別のクラス名を考えてそのクラス名を覚える必要はなく、Assertと入力するだけです。利用可能なメソッドを参照してください。
user420667

4
@KonradMorawski静的クラスの拡張メソッドを書くことは不可能です。stackoverflow.com/questions/249222/...
マーティン・ブラウン

2
@modiXもちろんです。ここで私を誤解しました。つまり、静的クラスの拡張メソッドを許可するだけ、user420667で説明されているシナリオで必要な機能を(将来的に)提供できるようになります。静的な継承は必要ありません。そのため、静的継承を導入するための非常に優れた根拠として彼のシナリオが私を襲いませんでした。この点でC#を変更する必要がある場合、マイナーな設計が実際に同じくらい優れているのに、どうしてメジャーな再設計を行うのか。
Konrad Morawski、2014

5
@ラーナーそれは本当に公平ではありません。ネットの中のすべての継承からobject、誰もがそれを知っていると予想されます。したがって、静的クラスは、明示的に指定するかどうかにかかわらず、常にから継承しobjectます。
RenniePet 2018

71

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

したがって、この:

static class Foo { }

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

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

17
あなたは静的が抽象+封印として実装されたと言っています。彼はなぜこれが行われたの知りたいのです。なぜ封印されているのですか?
ルーカス

22
これはすべての質問に答えるものではありません。それは彼が尋ねていた問題を単に述べ直します。
Igby Largeman、2010年

28
まあ、OPは「なぜ静的クラスがシールされているのか」ではなく、「なぜ静的クラスからシールされているのか」と尋ねるべきでした。その答えはもちろん「シールされているから」です。この答えが私の投票になります。
Alex Budovski

6
静的クラスがシールされたクラスとして自動的にコンパイルされることを共有してくれてありがとう-それがいつ/どのようにして起こったのかと思っていました!
マーク・

23
@AlexBudovski:この答えは正しいですが、迷子になった人に「どこにいるのですか」と尋ねられたときに「あなたは私の前にいる」と伝えるのと同じくらい役に立ちません。
DeepSpace101 2014

24

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

MyStaticType.MyStaticMember();

そのクラスから継承する場合は、新しいタイプ名でアクセスする必要があります。

MyNewType.MyStaticMember();

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

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

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

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

つまり、最終的には静的クラスを継承しても何も得られません。


5
拡張メソッドを介して既存の静的型にメソッドを追加することはできません。望ましい使用例については、受け入れられた回答に関する私のコメントを参照してください。(基本的に、MyNewTypeという名前をMyStaticTypeという名前に変更するだけなので、MyStaticType:OldProject.MyStaticType)
user420667

2
がのメンバーにSomeClass<T> where T:Fooアクセスできるように、静的タイプと仮想静的メンバーとの継承関係を定義できますFoo。また、のT一部の仮想静的メンバーをFooオーバーライドするクラスの場合、ジェネリッククラスはそれらのオーバーライドを使用します。おそらく、言語が現在のCLRと互換性のある方法でそれを行うことができる規則を定義することも可能です(たとえば、そのようなメンバーを持つクラスは、静的なフィールドとともに、そのようなインスタンスメンバーを含む保護された非静的クラスを定義する必要がありますそのタイプのインスタンスを保持します)。
スーパーキャット2012年

静的クラスを継承することは正しいことだと私が思う場合に気づきましたが、明らかにそれらにアクセスできます。生データストリームをプリンターに送信するための静的クラスがあります(工業用ラベルプリンターと通信するため)。Windows API宣言がたくさんあります。私は今、同じAPI呼び出しを必要とするプリンターをいじるための別のクラスを書いています。組み合わせる?良くない。それらを2回宣言しますか?醜い。継承しますか?良いですが、許可されていません。
Loren Pechtel

3

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


タイプとしてクラスを受け取る汎用ローダーがあります。そのクラスには、5つのヘルパーメソッドと、文字列(名前と説明)を返す2つのメソッドがあります。私はそれを間違って選んだかもしれませんが(私はそう思う)、私が見つけた唯一の解決策は、ジェネリックローダーでクラスをインスタンス化し、メソッドにアクセスすることでした... meh。
ANeves、2010年

代替案は巨大な辞書になると思います。また、「達成したいこと」を言うときは、代わりに「あなたが目指しているように見えるもの」などを言うべきです。
ANeves、2010年

3

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


2

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

静的メンバーを継承できず、静的クラスに静的メンバーのみを含めることができる場合、静的クラスを継承するとどのようなメリットがありますか?


1

うーん...静的メソッドで満たされた非静的クラスがあるだけなら、それははるかに異なりますか?


2
それは私に残されたものです。私はこのようにします。そして、私はそれが好きではありません。
ユーザー

私もこの方法でこれを行いましたが、オブジェクトをインスタンス化しないことがわかっていると、何らかの理由で美的に感じません。材料費がかからないにもかかわらず、私はいつもこのような細部に悩まされています。
gdbj 2015

静的メソッドで満たされた非静的クラスでは、拡張メソッドを配置することはできません:)
JakubSzułakiewicz19年

1

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

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

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

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

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静的として使用可能なています。これで、静的コンテキストに公開する必要がある各メソッドおよびプロパティの静的ラッパーを作成するために残された唯一のことです。

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

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


1

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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


-1

静的メンバーとプライベートコンストラクターのみを含む静的クラスを作成する場合、唯一の理由は、静的コンストラクターがクラスをインスタンス化できないため、静的クラスを継承できないためです。クラス名自体を使用して静的クラスを作成します。静的クラスを継承しようとすることはお勧めできません。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.