2010-02-19 21 views
39

Mögliche Duplizieren:
Why can’t I create an abstract constructor on an abstract C# class?Zusammenfassung Konstruktor in C#

Warum kann ich nicht erklären, abstrakt einen Konstruktor meiner Klasse wie folgt:

public abstract class MyClass { 
    public abstract MyClass(int param); 
} 
+3

Es scheint so, als ob der Konstruktor ein Implementierungsdetail ist und daher die Erzwingung, dass Unterklassen auf eine bestimmte Weise konstruiert werden, eine schlechte Sache wäre. Wenn Sie eine gekapselte Konstruktion wünschen, verwenden Sie das statische Fabrikmuster. –

+0

Sieht so aus, als wolle er etwas, was mit dem aktuellen Generics-, Struktur-Subtyping-/Parameter-Taking-Konstruktor nicht möglich ist. – Dykam

+0

Was möchten Sie erreichen? Vielleicht gibt es eine andere Sichtweise. – fre0n

Antwort

61

Konstruktoren sind nur auf die Klasse anwendbar, in der sie definiert sind, dh sie werden nicht vererbt. Es werden Basisklassenkonstruktoren verwendet (Sie müssen einen von ihnen aufrufen, auch wenn nur der Standard automatisch aufgerufen wird), aber nicht durch Ableiten von Klassen überschrieben. Sie können einen Konstruktor für eine abstrakte Basisklasse definieren - er kann nicht direkt verwendet werden, kann aber durch Ableiten von Klassen aufgerufen werden. Was Sie nicht tun können, ist, eine abgeleitete Klasse zu zwingen, eine bestimmte Konstruktorsignatur zu implementieren.

Es ist völlig in Ordnung, einen Konstruktor zu definieren, der normalerweise geschützt ist, um einen allgemeinen Setup-Code für alle abgeleiteten Klassen zu definieren. Dies trifft besonders dann zu, wenn die abstrakte Klasse ein anderes Standardverhalten bietet, das auf dieser Konfiguration beruht. Zum Beispiel:

public abstract class Foo 
{ 
    public string Name { get; private set; } 

    protected Foo(string name) 
    { 
     this.Name = name; 
    } 
} 

public class Bar : Foo 
{ 
    public Bar() : base("bar") 
    { 
     ... 
    } 
} 
+4

Sie irren sich nicht, aber ich würde eines klarstellen: Konstruktoren werden nicht vererbt, aber sie werden * von Kindern genannt. Ihre einzige Wahl ist * welcher * Konstruktor, um von Ihrem zu ketten. –

+0

Ich habe aktualisiert und geklärt. – tvanfosson

+0

@tvanfosson: _you wahrscheinlich bedeutete 'Bar' zu erben' Foo'_ – comecme

12

Sie können‘ t deklariere es abstract, aber Sie können einen Konstruktor für Ihre abstrakte Klasse haben; Entfernen Sie einfach das Wort abstract und stellen Sie einen Körper dafür bereit.

+14

... und einen Körper dafür bereitstellen. – tvanfosson

+4

Es ist auch eine gute Übung, Konstruktoren in abstrakten Klassen als ** protected ** zu deklarieren. Es erzwingt die Tatsache, dass die Klasse nicht direkt instanziiert werden kann. –

+0

@tvanfosson: Danke, dass du darauf hingewiesen hast. Das habe ich übersehen, als ich die Frage gelesen habe. –

0

Per Definition kann die Klasse nicht direkt instanziiert werden, in gewisser Weise ist sie bereits abstrakt.

+0

Abstrakte Methoden haben keinen Körper, bis sie von einer abgeleiteten Klasse bereitgestellt werden. Dies gilt definitiv nicht für Konstruktoren abstrakter Klassen. –

+0

Ich sehe deinen Standpunkt. Die Absicht der Frage bezog sich jedoch eher auf die Vererbung. –

1

Ein Konstruktor ist keine gewöhnliche Methode. Es hat einen speziellen Zweck und ist daher auf sprachliche Merkmale beschränkt, die für diesen Zweck sinnvoll sind. Siehe auch: Why do constructors not return values?

3

Weil abstrakte Konstruktoren nicht unterstützt werden.

Aber eine abstrakte Klasse kann einen Konstruktor haben.

3

was falsch mit diesem:

public abstract class MyClass { 
    protected MyClass(int param) 
    { 
    } 
} 

In diesem Fall, dass Sie alle abgeleiteten Klassen verpflichten Basisklassenkonstruktor nennen.

+1

Es zwingt die Klasse, einen Konstruktor zu deklarieren, aber nicht unbedingt einen, der einen Integer-Parameter braucht. Sie könnten es leicht als öffentliche Klasse deklarieren. NewClass: MyClass {public NewClass(): base (0) {}} ' – tvanfosson

+0

Sie können die Argumentüberprüfung im MyClass-Konstruktor verwenden und die Exception wegen eines ungültigen Arguments auslösen. Sie können jedoch nicht erzwingen, dass abgeleitete Klassen gültige Daten an den MyClass-Konstruktor übergeben und keine zusätzlichen Konstruktoren in abgeleiteten Klassen hinzufügen. –

+1

Kleine Klärung - sie sind nicht verpflichtet, sie zu nennen, aber sie dürfen es nennen. – slugster

6

Abstrakt bedeutet virtuell. Ein nicht standardmäßiger Konstruktor kann niemals polymorph aufgerufen werden, daher sind virtuelle und abstrakte Konstruktoren nicht zulässig.

Wenn in einer zukünftigen Version von C# Generics erweitert werden, um den Aufruf von Nichtstandardkonstruktoren über einen generischen Typparameter zu ermöglichen, wären polymorphe Aufrufe von Konstruktoren möglich, und virtuelle und abstrakte Konstruktoren könnten ebenfalls hinzugefügt werden.

+2

Das Aufrufen eines nicht standardmäßigen Konstruktors in einer generischen Methode ist immer noch kein polymorpher Aufruf, da der Typ im Voraus bekannt ist. Ein Konstruktor kann niemals polymorph, Punkt genannt werden. –

+0

@AntonTykhyy: Aufrufe im generischen Kontext * sind * polymorph. .NET Generics funktionieren auf der Basis von Typ-Löschung und virtuellem Versand. Sie sind nicht auf die Art spezialisiert, wie C++ - Vorlagen sind. –

+0

Das ist, wenn Sie Werttypen ignorieren. Generics closed over Werttypen _are_ spezialisiert. Stellen Sie sich vor, wie schrecklich 'List ' usw. sonst wäre. Aber ich verstehe was du meinst. Ich nehme an, man könnte einen Slot in 'RuntimeTypeHandle' für jeden Konstruktor hinzufügen, der in einer generischen Constraint verwendet wird. Was aber, wenn jemand später eine Assembly lädt, die eine neue generische Konstruktoreinschränkung hat? Alle vorhandenen 'RuntimeTypeHandle's müssten aktualisiert werden. Nicht zu sagen, dass es nicht getan werden kann, nur eine Menge Ärger. –

5

Konstruktoren sind näher an statischen Methoden als an "normalen" Methoden. Wie statische Methoden können sie überladen werden, aber nicht überschrieben. Das heißt, sie werden nicht vererbt, sondern können neu definiert werden.

public BaseClass 
{ 
    public BaseClass(String s) { ... } 
    public static void doIt (String s) { ... } 
} 

public SubClass extends BaseClass 
{ 
    public SubClass(String s) { ... } 
    public static void doIt (String s) { ... } 
} 

public SubClass2 extends BaseClass 
{ 
} 

new SubClass("hello"); 
SubClass.doIt("hello"); 

new SubClass2("hello"); // NOK 
SubClass2.doIt("hello"); // NOK 

Konstrukteurs und statische Methoden sind nie dynamisch (fast) geschickt - Sie wissen immer den konkreten Typ Sie oder die konkrete Klasse der statischen Methode instanziiert.Deshalb macht es keinen Sinn, abstrakten Konstruktor und abstrakte statische Methode zu haben. Aus diesem Grund können Sie in interfaces auch keinen Konstruktor und keine statische Methode angeben.

Sie auch von Konstruktor als statischer Factory-Methode denken können (und sehen die corresponding pattern):

MyClass obj = new MyClass(); // the way it is 
    MyClass obj = MyClass.new(); // think of it like this 

Der einzige Fall, den ich sehe, wo es Sinn machen würde abstrakte Konstruktor oder abstrakte statische Methode zu definieren wäre Wenn Reflexion verwendet wird. In diesem Fall können Sie sicherstellen, dass alle Unterklassen die entsprechende statische Methode oder den entsprechenden Konstruktor neu definieren. Aber Reflexion ist ein anderes Thema ...

Hinweis: In Sprachen wie Smalltalk, wo Klassen regelmäßige Objekte sind, können Sie statische Methode überschreiben und abstrakten Konstruktor haben. Aber es gilt nicht für Java, da Klassen keine "normalen" Objekte sind, selbst wenn Sie sie mit Reflektion erhalten können.

+0

+1 für den Reflektions-Anwendungsfall. – fre0n