Warum kann ich keine statischen Klassen erben?

224

Ich habe mehrere Klassen, die eigentlich keinen Staat brauchen. Aus organisatorischer Sicht möchte ich sie in eine Hierarchie einordnen.

Aber anscheinend kann ich keine Vererbung für statische Klassen deklarieren.

Sowas in der Art:

public static class Base
{
}

public static class Inherited : Base
{
}

wird nicht funktionieren.

Warum haben die Designer der Sprache diese Möglichkeit geschlossen?

Benutzer
quelle

Antworten:

174

Zitat von hier :

Dies ist eigentlich beabsichtigt. Es scheint keinen guten Grund zu geben, eine statische Klasse zu erben. Es verfügt über öffentliche statische Mitglieder, auf die Sie jederzeit über den Klassennamen selbst zugreifen können. Die einzigen Gründe, die ich für das Erben statischer Dinge gesehen habe, waren schlechte, wie das Speichern einiger Zeichen beim Tippen.

Es mag Gründe geben, Mechanismen in Betracht zu ziehen, um statische Elemente direkt in den Geltungsbereich zu bringen (und wir werden dies tatsächlich nach dem Orcas-Produktzyklus berücksichtigen), aber die Vererbung statischer Klassen ist nicht der richtige Weg: Es ist der falsche Mechanismus und funktioniert Nur für statische Mitglieder, die sich zufällig in einer statischen Klasse befinden.

(Mads Torgersen, PM in C # -Sprache)

Andere Meinungen von channel9

Die Vererbung in .NET funktioniert nur auf Instanzbasis. Statische Methoden werden auf Typebene und nicht auf Instanzebene definiert. Deshalb funktioniert das Überschreiben bei statischen Methoden / Eigenschaften / Ereignissen nicht ...

Statische Methoden werden nur einmal gespeichert. Es gibt keine virtuelle Tabelle usw., die für sie erstellt wurde.

Wenn Sie eine Instanzmethode in .NET aufrufen, geben Sie ihr immer die aktuelle Instanz an. Dies wird von der .NET-Laufzeit ausgeblendet, aber es passiert. Jede Instanzmethode hat als erstes Argument einen Zeiger (Verweis) auf das Objekt, auf dem die Methode ausgeführt wird. Dies ist bei statischen Methoden nicht der Fall (da sie auf Typebene definiert sind). Wie sollte der Compiler entscheiden, welche Methode aufgerufen werden soll?

(kleiner Guru)

Und als wertvolle Idee hat littleguru eine teilweise "Problemumgehung" für dieses Problem: das Singleton- Muster.

boj
quelle
92
Eigentlich mangelnde Vorstellungskraft auf Torgersens Seite. Ich hatte einen ziemlich guten Grund, dies tun zu wollen :)
Thorarin
10
Wie wäre es damit? Sie haben eine DLL, die nicht Open Source ist / Sie haben keinen Zugriff auf die Quelle, sagen wir NUnit. Sie möchten die Assert-Klasse um eine Methode wie Throws <> (Aktion a) erweitern (ja, das gibt es auch, aber an einem Punkt war es nicht so.) Sie können Ihrem Projekt eine Assert: NUnit.Framework.Assert hinzufügen Klasse, und fügen Sie dann eine Throws-Methode hinzu. Problem gelöst. Es ist sauberer, es auch im Code zu verwenden ... anstatt sich einen anderen Klassennamen einfallen zu lassen und sich diesen Klassennamen zu merken, geben Sie einfach Assert ein. und sehen Sie sich die verfügbaren Methoden an.
user420667
4
@KonradMorawski Es ist unmöglich, Erweiterungsmethoden für statische Klassen zu schreiben. stackoverflow.com/questions/249222/…
Martin Braun
2
@modiX Natürlich. Du hast mich hier falsch verstanden. Was ich damit gemeint habe ist, dass die in dem von user420667 beschriebenen Szenario erforderliche Funktionalität ( in Zukunft ) nur durch das Zulassen von Erweiterungsmethoden für statische Klassen bereitgestellt werden kann. Keine statische Vererbung erforderlich. Deshalb hat mich sein Szenario nicht als sehr gute Begründung für die Einführung einer statischen Vererbung angesehen. Wenn C # in diesem Aspekt geändert werden sollte, warum sollte man sich dann für eine größere Neugestaltung entscheiden, wenn eine kleinere in der Praxis genauso gut wäre?
Konrad Morawski
5
@Learner Das ist nicht wirklich fair. In .Net erbt alles von object, und jeder muss das wissen. Statische Klassen erben also immer von object, unabhängig davon , ob Sie es explizit angeben oder nicht.
RenniePet
71

Der Hauptgrund, warum Sie eine statische Klasse nicht erben können, besteht darin, dass sie abstrakt und versiegelt sind (dies verhindert auch, dass eine Instanz von ihnen erstellt wird).

Also das:

static class Foo { }

kompiliert zu dieser IL:

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }
Andrew Hare
quelle
17
Sie sagen, dass statisch als abstrakt + versiegelt implementiert wurde. Er möchte wissen, warum dies getan wurde. Warum ist es versiegelt?
Lucas
22
Dies beantwortet nicht die Frage alles; es wiederholt nur das Problem, nach dem er gefragt hat.
Igby Largeman
27
Nun, das OP hätte fragen sollen: "Warum sind statische Klassen versiegelt?" Und nicht "Warum kann ich nicht von statischen Klassen erben?". Die Antwort lautet natürlich "weil sie versiegelt sind" . Diese Antwort bekommt meine Stimme.
Alex Budovski
6
Vielen Dank für das Teilen, dass statische Klassen automatisch als versiegelte Klassen kompiliert werden - ich habe mich gefragt, wie / wann das passiert ist!
Mark
22
@AlexBudovski: Diese Antwort ist richtig, aber so nutzlos, als würde man einer verlorenen Person sagen "Du bist vor mir", wenn sie dich fragt "Wo bin ich?".
DeepSpace101
24

Stellen Sie sich das so vor: Sie greifen über den Typnamen wie folgt auf statische Elemente zu:

MyStaticType.MyStaticMember();

Wenn Sie von dieser Klasse erben würden, müssten Sie über den neuen Typnamen darauf zugreifen:

MyNewType.MyStaticMember();

Daher hat das neue Element bei Verwendung im Code keine Beziehung zum Original. Es gibt keine Möglichkeit, eine Vererbungsbeziehung für Dinge wie Polymorphismus auszunutzen.

Vielleicht denken Sie, Sie möchten nur einige der Elemente in der ursprünglichen Klasse erweitern. In diesem Fall hindert Sie nichts daran, nur ein Mitglied des Originals in einem völlig neuen Typ zu verwenden.

Vielleicht möchten Sie einem vorhandenen statischen Typ Methoden hinzufügen. Sie können dies bereits über Erweiterungsmethoden tun.

Vielleicht möchten Sie in der Lage sein, zur TypeLaufzeit eine Statik an eine Funktion zu übergeben und eine Methode für diesen Typ aufzurufen, ohne genau zu wissen, was die Methode tut. In diesem Fall können Sie eine Schnittstelle verwenden.

Am Ende gewinnt man also nichts, wenn man statische Klassen erbt.

Joel Coehoorn
quelle
5
Sie können einem vorhandenen statischen Typ keine Methoden über eine Erweiterungsmethode hinzufügen. In meinem Kommentar zur akzeptierten Antwort finden Sie ein Beispiel für die gewünschte Verwendung. (Grundsätzlich möchten Sie einfach das, was Sie MyNewType genannt haben, in MyStaticType umbenennen, also MyStaticType: OldProject.MyStaticType)
user420667
2
Man könnte Vererbungsbeziehungen mit statischen Typen und virtuellen statischen Mitgliedern so definieren, dass a SomeClass<T> where T:Fooauf Mitglieder von zugreifen kann Foo, und wenn Teine Klasse einige virtuelle statische Mitglieder Fooüberschreibt, würde die generische Klasse diese Überschreibungen verwenden. Es wäre wahrscheinlich sogar möglich, eine Konvention zu definieren, über die Sprachen dies auf eine Weise tun könnten, die mit der aktuellen CLR kompatibel ist (z. B. sollte eine Klasse mit solchen Mitgliedern eine geschützte nicht statische Klasse definieren, die solche Instanzmitglieder zusammen mit einem statischen Feld enthält eine Instanz dieses Typs halten).
Supercat
Ich befand mich in einem Fall, in dem ich glaube, dass das Erben einer statischen Klasse das Richtige ist, obwohl Sie offensichtlich trotzdem darauf zugreifen können. Ich habe eine statische Klasse zum Senden von Rohdatenströmen an den Drucker (zum Sprechen mit industriellen Etikettendruckern). Es enthält eine Reihe von Windows-API-Deklarationen. Ich schreibe jetzt eine weitere Klasse, um mit Druckern zu spielen, die dieselben API-Aufrufe benötigen. Kombinieren? Nicht gut. Zweimal deklarieren? Hässlich. Erben? Gut, aber nicht erlaubt.
Loren Pechtel
3

Was Sie mithilfe der Klassenhierarchie erreichen möchten, kann lediglich durch Namespace erreicht werden. Sprachen, die Namespapiere unterstützen (wie C #), können daher keine Klassenhierarchie statischer Klassen implementieren. Da Sie keine der Klassen instanziieren können, benötigen Sie lediglich eine hierarchische Organisation der Klassendefinitionen, die Sie mithilfe von Namespaces erhalten können

Amit
quelle
Ich habe einen generischen Loader, der eine Klasse als Typ empfängt. Diese Klasse verfügt über 5 Hilfsmethoden und 2 Methoden, die eine Zeichenfolge (Name und Beschreibung) zurückgeben. Ich hätte es vielleicht falsch gewählt (ich denke schon), aber die einzige Lösung, die ich gefunden habe, war, die Klasse im generischen Loader zu instanziieren, um auf die Methoden zuzugreifen ... meh.
Aneves
Die Alternative wäre wohl ein riesiges Wörterbuch. Wenn Sie "was Sie erreichen möchten" sagen, sollten Sie stattdessen "was Sie anscheinend anstreben" oder ähnliches sagen.
Aneves
3

Sie können stattdessen Komposition verwenden. Auf diese Weise können Sie vom statischen Typ aus auf Klassenobjekte zugreifen. Aber kann immer noch keine Schnittstellen oder abstrakten Klassen implementieren

Yogesh Karekar
quelle
2

Obwohl Sie über den Namen der geerbten Klassen auf "geerbte" statische Mitglieder zugreifen können, werden statische Mitglieder nicht wirklich geerbt. Dies ist teilweise der Grund, warum sie nicht virtuell oder abstrakt sein können und nicht überschrieben werden können. Wenn Sie in Ihrem Beispiel Base.Method () deklariert haben, ordnet der Compiler einen Aufruf von Inherited.Method () ohnehin Base.Method () zu. Sie können Base.Method () auch explizit aufrufen. Sie können einen kleinen Test schreiben und das Ergebnis mit Reflector sehen.

Also ... wenn Sie keine statischen Elemente erben können und wenn statische Klassen nur statische Elemente enthalten können , was würde das Erben einer statischen Klasse nützen?

Lucas
quelle
1

Hmmm ... wäre es viel anders, wenn Sie nur nicht statische Klassen mit statischen Methoden hätten ...?

CookieOfFortune
quelle
2
Das bleibt mir übrig. Ich mache es so. Und ich mag es nicht.
Benutzer
Ich habe es auch so gemacht, aber aus irgendeinem Grund fühlt es sich nicht ästhetisch an, wenn Sie wissen, dass Sie das Objekt niemals instanziieren werden. Ich quäle mich immer mit solchen Details, obwohl es keine materiellen Kosten dafür gibt.
GDBJ
In nicht statischen Klassen mit statischen Methoden können Sie keine Erweiterungsmethoden setzen :)
Jakub Szułakiewicz
1

Eine Problemumgehung, die Sie tun können, besteht darin, keine statischen Klassen zu verwenden, sondern den Konstruktor auszublenden, sodass nur die statischen Klassenelemente außerhalb der Klasse zugänglich sind. Das Ergebnis ist im Wesentlichen eine vererbbare "statische" Klasse:

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>();

Leider können wir aufgrund der Sprachbeschränkung "by-design" Folgendes nicht tun:

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

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

Sie können etwas tun, das wie statische Vererbung aussieht.

Hier ist der Trick:

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

Dann können Sie dies tun:

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

    public void MethodA()
    {
    }
}

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

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

Die InheritedKlasse selbst ist nicht statisch, aber wir erlauben nicht, sie zu erstellen. Es hat tatsächlich einen statischen Konstruktor geerbt, der erstellt Base, sowie alle Eigenschaften und Methoden vonBase sind als statisch verfügbar. Jetzt müssen Sie nur noch statische Wrapper für jede Methode und Eigenschaft erstellen, die Sie für Ihren statischen Kontext verfügbar machen müssen.

Es gibt Nachteile wie die Notwendigkeit der manuellen Erstellung statischer Wrapper-Methoden und newSchlüsselwörter. Dieser Ansatz unterstützt jedoch etwas, das der statischen Vererbung sehr ähnlich ist.

PS Wir haben dies zum Erstellen kompilierter Abfragen verwendet, und dies kann tatsächlich durch ConcurrentDictionary ersetzt werden, aber ein statisches schreibgeschütztes Feld mit seiner Thread-Sicherheit war gut genug.

Konard
quelle
1

Meine Antwort: schlechte Designauswahl. ;-);

Dies ist eine interessante Debatte über die Auswirkungen auf die Syntax. Der Kern des Arguments ist meiner Ansicht nach, dass eine Entwurfsentscheidung zu versiegelten statischen Klassen führte. Ein Fokus auf Transparenz der Namen der statischen Klasse, die auf der obersten Ebene angezeigt werden, anstatt sich hinter untergeordneten Namen zu verstecken ("verwirrend")? Man kann sich eine Sprachimplementierung vorstellen, die verwirrend direkt auf die Basis oder das Kind zugreifen könnte.

Ein Pseudobeispiel unter der Annahme einer statischen Vererbung wurde auf irgendeine Weise definiert.

public static class MyStaticBase
{
    SomeType AttributeBase;
}

public static class MyStaticChild : MyStaticBase
{
    SomeType AttributeChild;
}

würde führen zu:

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

was könnte (würde?) den gleichen Speicher wie beeinflussen

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

Sehr verwirrend!

Aber warte! Wie würde der Compiler damit umgehen, dass MyStaticBase und MyStaticChild in beiden dieselbe Signatur definiert haben? Wenn das Kind überschreibt, als mein obiges Beispiel NICHT den gleichen Speicher ändern würde, vielleicht? Dies führt zu noch mehr Verwirrung.

Ich glaube, es gibt eine starke Rechtfertigung für den Informationsraum für eine begrenzte statische Vererbung. Mehr zu den Grenzen in Kürze. Dieser Pseudocode zeigt den Wert:

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(){/*...*/};
}

Dann bekommen Sie:

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...
} 

Die Verwendung sieht aus wie:

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

Die Person, die das statische Kind erstellt, muss nicht über den Serialisierungsprozess nachdenken, solange sie die Einschränkungen versteht, die für die Serialisierungs-Engine der Plattform oder Umgebung gelten können.

Statik (Singletons und andere Formen von "Globals") tritt häufig beim Konfigurationsspeicher auf. Durch statische Vererbung kann diese Art der Zuweisung von Verantwortlichkeiten in der Syntax sauber dargestellt werden, um einer Hierarchie von Konfigurationen zu entsprechen. Wie ich gezeigt habe, besteht jedoch ein großes Potenzial für massive Mehrdeutigkeiten, wenn grundlegende statische Vererbungskonzepte implementiert werden.

Ich glaube, die richtige Wahl für das Design wäre, eine statische Vererbung mit bestimmten Einschränkungen zuzulassen:

  1. Kein Überschreiben von irgendetwas. Das untergeordnete Element kann die Basisattribute, -felder oder -methoden usw. nicht ersetzen. Das Überladen sollte in Ordnung sein, solange es einen Unterschied in der Signatur gibt, der es dem Compiler ermöglicht, untergeordnetes Element gegen Basis zu sortieren.
  2. Erlaube nur generische statische Basen, du kannst nicht von einer nicht generischen statischen Basis erben.

Sie können denselben Speicher weiterhin über eine generische Referenz ändern MyStaticBase<ChildPayload>.SomeBaseField. Sie wären jedoch entmutigt, da der generische Typ angegeben werden müsste. Während die untergeordnete Referenz sauberer wäre:MyStaticChild.SomeBaseField .

Ich bin kein Compiler-Autor, daher bin ich mir nicht sicher, ob mir etwas über die Schwierigkeiten bei der Implementierung dieser Einschränkungen in einem Compiler fehlt. Trotzdem bin ich fest davon überzeugt, dass ein Informationsraumbedarf für eine begrenzte statische Vererbung besteht, und die grundlegende Antwort lautet, dass dies aufgrund einer schlechten (oder zu einfachen) Designauswahl nicht möglich ist.

Dale Strickler
quelle
0

Statische Klassen und Klassenmitglieder werden verwendet, um Daten und Funktionen zu erstellen, auf die zugegriffen werden kann, ohne eine Instanz der Klasse zu erstellen. Statische Klassenmitglieder können verwendet werden, um Daten und Verhalten unabhängig von der Objektidentität zu trennen: Die Daten und Funktionen ändern sich nicht, unabhängig davon, was mit dem Objekt geschieht. Statische Klassen können verwendet werden, wenn die Klasse keine Daten oder Verhaltensweisen enthält, die von der Objektidentität abhängen.

Eine Klasse kann als statisch deklariert werden, was darauf hinweist, dass sie nur statische Elemente enthält. Es ist nicht möglich, das neue Schlüsselwort zum Erstellen von Instanzen einer statischen Klasse zu verwenden. Statische Klassen werden automatisch von der Common Language Runtime (CLR) von .NET Framework geladen, wenn das Programm oder der Namespace geladen wird, der die Klasse enthält.

Verwenden Sie eine statische Klasse, um Methoden zu enthalten, die keinem bestimmten Objekt zugeordnet sind. Beispielsweise ist es häufig erforderlich, eine Reihe von Methoden zu erstellen, die nicht auf Instanzdaten reagieren und keinem bestimmten Objekt in Ihrem Code zugeordnet sind. Sie können eine statische Klasse verwenden, um diese Methoden zu speichern.

Im Folgenden sind die Hauptmerkmale einer statischen Klasse aufgeführt:

  1. Sie enthalten nur statische Elemente.

  2. Sie können nicht instanziiert werden.

  3. Sie sind versiegelt.

  4. Sie dürfen keine Instanzkonstruktoren enthalten (C # -Programmierhandbuch).

Das Erstellen einer statischen Klasse entspricht daher im Wesentlichen dem Erstellen einer Klasse, die nur statische Elemente und einen privaten Konstruktor enthält. Ein privater Konstruktor verhindert, dass die Klasse instanziiert wird.

Der Vorteil der Verwendung einer statischen Klasse besteht darin, dass der Compiler überprüfen kann, ob keine Instanzmitglieder versehentlich hinzugefügt wurden. Der Compiler garantiert, dass Instanzen dieser Klasse nicht erstellt werden können.

Statische Klassen sind versiegelt und können daher nicht vererbt werden. Sie können von keiner Klasse außer Object erben. Statische Klassen dürfen keinen Instanzkonstruktor enthalten. Sie können jedoch einen statischen Konstruktor haben. Weitere Informationen finden Sie unter Statische Konstruktoren (C # -Programmierhandbuch).

Zone
quelle
-1

Wenn wir eine statische Klasse erstellen, die nur die statischen Elemente und einen privaten Konstruktor enthält. Der einzige Grund ist, dass der statische Konstruktor die Instanziierung der Klasse verhindert, da wir keine statische Klasse erben können. Die einzige Möglichkeit, auf das Mitglied der zuzugreifen statische Klasse unter Verwendung des Klassennamens selbst. Der Versuch, eine statische Klasse zu erben, ist keine gute Idee.

Soumen Chakraborty
quelle