Warum kann ich in C # keine abstrakten statischen Methoden haben?

181

Ich habe in letzter Zeit einiges mit Anbietern zusammengearbeitet und bin auf eine interessante Situation gestoßen, in der ich eine abstrakte Klasse mit einer abstrakten statischen Methode haben wollte. Ich habe ein paar Beiträge zu diesem Thema gelesen und es hat irgendwie Sinn gemacht, aber gibt es eine schöne klare Erklärung?

Lomaxx
quelle
3
Bitte lassen Sie diese offen, um zukünftige Verbesserungen zu ermöglichen.
Mark Biek
6
Ich denke, die Frage ist, dass C # für genau diese Situation ein anderes Schlüsselwort benötigt. Sie möchten eine Methode, deren Rückgabewert nur vom Typ abhängt, für den sie aufgerufen wird. Sie können es nicht "statisch" nennen, wenn der Typ unbekannt ist. Sobald der Typ bekannt ist, wird er statisch. "Ungelöste statische Aufladung" ist die Idee - sie ist noch nicht statisch, aber sobald wir den empfangenden Typ kennen, wird es so sein. Dies ist ein perfektes Konzept, weshalb Programmierer immer wieder danach fragen. Aber es passte nicht ganz in die Art und Weise, wie die Designer über die Sprache dachten.
William Jockusch
@WilliamJockusch was bedeutet Empfangstyp? Wenn ich BaseClass.StaticMethod () aufrufe, ist BaseClass der einzige Typ, mit dem die Entscheidung getroffen werden kann. Auf dieser Ebene ist es jedoch abstrakt, sodass die Methode nicht aufgelöst werden kann. Wenn Sie stattdessen DerivedClass.StaticMethod gut aufrufen, ist die Basisklasse irrelevant.
Martin Capodici
In der Basisklasse ist die Methode nicht aufgelöst und kann nicht verwendet werden. Sie benötigen entweder einen abgeleiteten Typ oder ein Objekt (das wiederum einen abgeleiteten Typ haben würde). Sie sollten baseClassObject.Method () oder DerivedClass.Method () aufrufen können. Sie können BaseClass.Method () nicht aufrufen, da Sie dadurch nicht den Typ erhalten.
William Jockusch
Mögliches Duplikat von Wie werden virtuelle statische Eigenschaften implementiert?
Peterh - Wiedereinsetzung Monica

Antworten:

156

Statische Methoden werden nicht als solche instanziiert , sondern sind nur ohne Objektreferenz verfügbar.

Ein Aufruf einer statischen Methode erfolgt über den Klassennamen, nicht über eine Objektreferenz, und der IL-Code (Intermediate Language) zum Aufrufen ruft die abstrakte Methode über den Namen der Klasse auf, die sie definiert hat, nicht unbedingt über den Namen von die Klasse, die Sie verwendet haben.

Lassen Sie mich ein Beispiel zeigen.

Mit folgendem Code:

public class A
{
    public static void Test()
    {
    }
}

public class B : A
{
}

Wenn Sie B.Test wie folgt aufrufen:

class Program
{
    static void Main(string[] args)
    {
        B.Test();
    }
}

Dann lautet der tatsächliche Code in der Main-Methode wie folgt:

.entrypoint
.maxstack 8
L0000: nop 
L0001: call void ConsoleApplication1.A::Test()
L0006: nop 
L0007: ret 

Wie Sie sehen, wird A.Test aufgerufen, da es die A-Klasse war, die ihn definiert hat, und nicht B.Test, obwohl Sie den Code auf diese Weise schreiben können.

Wenn Sie Klassentypen wie in Delphi hätten, in denen Sie eine Variable erstellen können, die sich auf einen Typ und nicht auf ein Objekt bezieht, hätten Sie mehr Verwendung für virtuelle und damit abstrakte statische Methoden (und auch Konstruktoren), aber diese sind nicht verfügbar und Daher sind statische Aufrufe in .NET nicht virtuell.

Mir ist klar, dass die IL-Designer zulassen könnten, dass der Code kompiliert wird, um B.Test aufzurufen, und den Aufruf zur Laufzeit auflösen, aber er wäre immer noch nicht virtuell, da Sie dort immer noch einen Klassennamen schreiben müssten.

Virtuelle und damit abstrakte Methoden sind nur dann nützlich, wenn Sie eine Variable verwenden, die zur Laufzeit viele verschiedene Objekttypen enthalten kann, und Sie daher die richtige Methode für das aktuelle Objekt in der Variablen aufrufen möchten. Bei statischen Methoden müssen Sie ohnehin einen Klassennamen durchlaufen, sodass die genaue aufzurufende Methode zur Kompilierungszeit bekannt ist, da sie sich nicht ändern kann und wird.

Daher sind virtuelle / abstrakte statische Methoden in .NET nicht verfügbar.

Lasse V. Karlsen
quelle
4
In Kombination mit der Art und Weise, wie das Überladen von Operatoren in C # erfolgt, wird leider die Möglichkeit ausgeschlossen, dass Unterklassen erforderlich sind, um eine Implementierung für eine bestimmte Operatorüberladung bereitzustellen.
Chris Moschini
23
Ich glaube nicht , diese Antwort besonders nützlich , da die Definition finden , Test()in ist Aeher als abstrakt zu sein und möglicherweise definiert in B\.
5
Generische Typparameter verhalten sich effektiv wie nicht persistente "Typ" -Variablen, und virtuelle statische Methoden könnten in einem solchen Kontext nützlich sein. Wenn man beispielsweise einen CarTyp mit einer virtuellen statischen CreateFromDescriptionFactory-Methode hatte, konnte Code, der einen Careingeschränkten generischen Typ akzeptierte, Taufgerufen werden T.CreateFromDescription, um ein Auto vom Typ zu erzeugen T. Ein solches Konstrukt könnte innerhalb der CLR ziemlich gut unterstützt werden, wenn jeder Typ, der eine solche Methode definiert, eine statische Singleton-Instanz einer verschachtelten Klasse generisch enthält, die die virtuellen "statischen" Methoden enthält.
Supercat
45

Statische Methoden können nicht vererbt oder überschrieben werden, weshalb sie nicht abstrakt sein können. Da statische Methoden für den Typ und nicht für die Instanz einer Klasse definiert sind, müssen sie für diesen Typ explizit aufgerufen werden. Wenn Sie also eine Methode für eine untergeordnete Klasse aufrufen möchten, müssen Sie ihren Namen verwenden, um sie aufzurufen. Dies macht die Vererbung irrelevant.

Angenommen, Sie könnten für einen Moment statische Methoden erben. Stellen Sie sich dieses Szenario vor:

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

Wenn Sie Base.GetNumber () aufrufen, welche Methode würde aufgerufen? Welcher Wert wurde zurückgegeben? Es ist ziemlich leicht zu erkennen, dass die Vererbung ohne das Erstellen von Instanzen von Objekten ziemlich schwierig ist. Abstrakte Methoden ohne Vererbung sind nur Methoden, die keinen Körper haben und daher nicht aufgerufen werden können.

David Wengier
quelle
33
In Anbetracht Ihres Szenarios würde ich sagen, dass Base.GetNumber () 5 zurückgeben würde; Child1.GetNumber () gibt 1 zurück; Child2.GetNumber () gibt 2 zurück; Können Sie mir das Gegenteil beweisen, um mir zu helfen, Ihre Argumentation zu verstehen? Vielen Dank
Luis Filipe
Das Gesicht, von dem Sie glauben, dass Base.GetNumber () 5 zurückgibt, bedeutet, dass Sie bereits verstehen, was los ist. Durch die Rückgabe des Basiswerts findet keine Vererbung statt.
David Wengier
60
Warum in aller Welt würde Base.GetNumber () etwas anderes als 5 zurückgeben? Es ist eine Methode in der Basisklasse - dort gibt es nur eine Option.
Artem Russakovskii
4
@ ArtemRussakovskii: Angenommen, man hatte int DoSomething<T>() where T:Base {return T.GetNumber();}. Es erscheint nützlich, wenn DoSomething<Base>()fünf zurückgegeben werden könnten, während DoSomething<Child2>()zwei zurückgegeben werden könnten . Eine solche Fähigkeit wäre nicht nur für Spielzeugbeispiele nützlich, sondern auch für etwas class Car {public static virtual Car Build(PurchaseOrder PO);}, bei dem jede Klasse, von Carder abgeleitet wird, eine Methode definieren müsste, mit der eine Instanz bei einer Bestellung erstellt werden könnte.
Supercat
4
Es gibt genau das gleiche "Problem" bei der nicht statischen Vererbung.
Ark-Kun
18

Ein anderer Befragter (McDowell) sagte, dass Polymorphismus nur für Objektinstanzen funktioniert. Das sollte qualifiziert sein; Es gibt Sprachen, die Klassen als Instanzen eines Typs "Klasse" oder "Metaklasse" behandeln. Diese Sprachen unterstützen den Polymorphismus sowohl für Instanz- als auch für Klassenmethoden (statisch).

C # ist wie Java und C ++ zuvor keine solche Sprache. Das staticSchlüsselwort wird explizit verwendet, um anzuzeigen, dass die Methode eher statisch als dynamisch / virtuell gebunden ist.

Chris Hanson
quelle
9

Hier ist eine Situation, in der definitiv eine Vererbung für statische Felder und Methoden erforderlich ist:

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"?
user275801
quelle
4
Nein, es gibt nur eine Instanz des Arrays 'Beine'. Die Ausgabe ist nicht deterministisch, da Sie nicht wissen, in welcher Reihenfolge die statischen Konstruktoren aufgerufen werden (es gibt tatsächlich keine Garantie dafür, dass der statische Konstruktor der Basisklasse überhaupt aufgerufen wird). "Bedürfnis" ist ein ziemlich absoluter Begriff, bei dem "Verlangen" wahrscheinlich genauer ist.
Sam
legssollte eine statische abstrakte Eigenschaft sein.
Little Endian
8

Um die vorherigen Erläuterungen zu ergänzen, werden statische Methodenaufrufe zur Kompilierungszeit an eine bestimmte Methode gebunden , wodurch polymorphes Verhalten eher ausgeschlossen wird.

Rytmis
quelle
C # ist statisch typisiert; Aufrufe an polymorphe Methoden werden meines Wissens auch zur Kompilierungszeit gebunden - das heißt, die CLR muss nicht entscheiden, welche Methode zur Laufzeit aufgerufen werden soll.
Adam Tolley
Wie genau funktioniert Polymorphismus Ihrer Meinung nach auf der CLR? Ihre Erklärung hat gerade den Versand virtueller Methoden ausgeschlossen.
Rytmis
Das ist kein wirklich nützlicher Kommentar, wie er sein könnte. Ich habe (mit "so wie ich es verstehe") nützlichen Diskurs eingeladen. Ich denke, Sie könnten vielleicht etwas mehr Inhalt bereitstellen - da die Leute hierher kommen, um nach Antworten und nicht nach Beleidigungen zu suchen. Obwohl es so aussieht, als ob ich mich derselben Sache schuldig machen könnte - ich meinte den obigen Kommentar wirklich als eine Frage: Bewertet C # diese Dinge nicht zur Kompilierungszeit?
Adam Tolley
Entschuldigung, ich meinte keine Beleidigung (obwohl ich zugebe, ein bisschen schnell zu antworten ;-). Der Punkt meiner Frage war, ob Sie diese Klassen haben: class Base {public virtual void Method (); } class Abgeleitet: Base {public override void Method (); } und schreibe so: Basisinstanz = new Derived (); instance.Method (); Die Informationen zum Typ der Kompilierungszeit auf der Aufrufsite lauten, dass wir eine Instanz von Base haben, wenn die tatsächliche Instanz eine abgeleitete Instanz ist. Der Compiler kann daher die genaue aufzurufende Methode nicht auflösen. Stattdessen gibt es eine "callvirt" IL-Anweisung aus, die die Laufzeit zum Versenden
anweist
1
Danke Mann, das ist informativ! Ich schätze, ich habe den Tauchgang in IL lange genug verschoben, wünsche mir viel Glück.
Adam Tolley
5

Wir überschreiben tatsächlich statische Methoden (in Delphi), es ist ein bisschen hässlich, aber es funktioniert gut für unsere Bedürfnisse.

Wir verwenden es, damit die Klassen eine Liste ihrer verfügbaren Objekte ohne die Klasseninstanz haben können. Wir haben beispielsweise eine Methode, die folgendermaßen aussieht:

class function AvailableObjects: string; override;
begin
  Result := 'Object1, Object2';
end; 

Es ist hässlich, aber notwendig. Auf diese Weise können wir genau das instanziieren, was benötigt wird, anstatt alle Klassen instanziieren zu lassen, nur um nach den verfügbaren Objekten zu suchen.

Dies war ein einfaches Beispiel, aber die Anwendung selbst ist eine Client-Server-Anwendung, bei der alle Klassen auf nur einem Server verfügbar sind und mehrere verschiedene Clients, die möglicherweise nicht alles benötigen, was der Server hat, und niemals eine Objektinstanz benötigen.

Dies ist also viel einfacher zu warten, als für jeden Client eine andere Serveranwendung zu haben.

Hoffe das Beispiel war klar.

Fabio Gomes
quelle
0

Die abstrakten Methoden sind implizit virtuell. Abstrakte Methoden erfordern eine Instanz, statische Methoden haben jedoch keine Instanz. Sie können also eine statische Methode in einer abstrakten Klasse haben, sie kann einfach nicht statisch abstrakt (oder abstrakt statisch) sein.

AMing
quelle
1
-1 Virtuelle Methoden benötigen keine Instanz, außer vom Design her. Und Sie sprechen die Frage nicht wirklich an, sondern lenken sie ab.
FallenAvatar