2010-02-19 13 views
39

Possible en double:
Why can’t I create an abstract constructor on an abstract C# class?constructeur Résumé en C#

Pourquoi je ne peux pas déclarer abstraite un constructeur de ma classe comme ceci:

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

Il semble que le constructeur soit un détail de mise en œuvre, et donc forcer la construction de sous-classes d'une certaine manière serait une mauvaise chose. Si vous voulez une construction encapsulée, utilisez le modèle d'usine statique. –

+0

On dirait qu'il veut quelque chose qui n'est pas possible avec les génériques actuels, le constructeur de sous-typage/prise de paramètres structurels. – Dykam

+0

Qu'est-ce que vous essayez d'accomplir? Peut-être y a-t-il une autre façon de voir les choses. – fre0n

Répondre

61

Les constructeurs s'appliquent uniquement à la classe dans laquelle ils sont définis, c'est-à-dire qu'ils ne sont pas hérités. Les constructeurs de classe de base sont utilisés (vous devez appeler l'un d'entre eux, même si vous appelez celui par défaut automatiquement) mais pas surchargé en dérivant des classes. Vous pouvez définir un constructeur sur une classe de base abstraite - il ne peut pas être utilisé directement, mais peut être appelé en dérivant des classes. Ce que vous ne pouvez pas faire est de forcer une classe dérivée à implémenter une signature de constructeur spécifique.

Il est parfaitement raisonnable d'avoir un constructeur défini, typiquement comme protégé, afin de définir un code de configuration commun pour toutes les classes dérivées. Cela est particulièrement vrai, peut-être, lorsque la classe abstraite fournit un autre comportement par défaut qui repose sur cette configuration. Par exemple:

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

Vous n'avez pas tort, mais je voudrais clarifier une chose: les constructeurs ne sont pas hérités, mais ils * sont * appelés par les enfants. Votre seul choix est * quel * constructeur à enchaîner à partir du vôtre. –

+0

J'ai mis à jour et clarifié. – tvanfosson

+0

@tvanfosson: _vous vouliez probablement dire 'Bar' pour hériter' Foo'_ – comecme

12

Vous pouvez » t le déclarer abstract, mais vous pouvez avoir un constructeur sur votre classe abstraite; il suffit de retirer le mot abstract et de fournir un corps pour cela.

+14

... et fournir un corps pour cela. – tvanfosson

+4

Il est également recommandé de déclarer les constructeurs dans les classes abstraites comme étant ** protected **. Cela renforce le fait que la classe ne peut pas être directement instanciée. –

+0

@tvanfosson: Merci d'avoir signalé cela. J'ai raté ça quand j'ai lu la question. –

0

Par définition, la classe ne peut pas être instanciée directement, donc dans un sens, elle est déjà abstraite.

+0

Les méthodes abstraites n'ont pas de corps tant qu'elles ne sont pas fournies par une classe dérivée. Ce n'est certainement pas le cas des constructeurs de classes abstraites. –

+0

Je vois ce que vous voulez dire. Cependant, l'intention de la question était plus liée à l'héritage. –

1

Un constructeur n'est pas une méthode ordinaire. Il a un but spécial, et est donc limité aux caractéristiques du langage qui ont un sens à cet effet. Voir aussi: Why do constructors not return values?

3

Parce que les constructeurs abstraits ne sont pas pris en charge.

Mais une classe abstraite peut avoir un constructeur.

3

Quel mal avec ceci:

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

Dans ce cas, vous obligez toutes les classes dérivées d'appeler le constructeur de classe de base.

+1

Cela force la classe à déclarer un constructeur, mais pas nécessairement un avec qui prend un paramètre entier. Vous pouvez facilement le déclarer comme public class NewClass: MyClass {public NewClass(): base (0) {}} ' – tvanfosson

+0

Vous pouvez utiliser la validation d'argument dans le constructeur MyClass et lancer une exception en raison d'un argument non valide. Mais vous ne pouvez pas forcer les classes dérivées à transmettre des données valides au constructeur MyClass et n'ajouter aucun constructeur supplémentaire dans les classes dérivées. –

+1

Petite précision - ils ne sont pas obligés de l'appeler, mais ils peuvent l'appeler. – slugster

6

Résumé implique virtuel. Un constructeur non défini par défaut ne peut jamais être appelé de manière polymorphe, de sorte que le virtuel et l'abstrait ne sont pas autorisés sur les constructeurs. Si dans une future version de C#, les génériques sont améliorés pour permettre d'appeler des constructeurs non-par défaut via un paramètre de type générique, des appels polymorphes aux constructeurs seraient possibles et des constructeurs virtuels et abstraits pourraient aussi être ajoutés.

+2

L'appel d'un constructeur autre que celui par défaut dans une méthode générique n'est toujours pas un appel polymorphe, car le type est connu à l'avance. Un constructeur ne peut jamais être appelé polymorphiquement, période. –

+0

@AntonTykhyy: Les appels dans un contexte générique * sont * polymorphes. Les génériques .NET fonctionnent sur la base de l'effacement de type et de l'envoi virtuel. Ils ne sont pas spécialisés par type comme les templates C++. –

+0

C'est si vous ignorez les types de valeur. Génériques fermés sur les types de valeur _are_ spécialisés. Imaginez comment horrible 'Liste ' etc serait autrement. Mais je vois ce que tu veux dire. Je suppose que l'on pourrait ajouter un slot dans RuntimeTypeHandle pour chaque constructeur utilisé dans une contrainte générique. Mais que se passe-t-il si quelqu'un charge un assembly qui a une nouvelle contrainte de constructeur générique? Tous les 'RuntimeTypeHandle' existants devraient être mis à jour. Ne pas le dire ne peut pas être fait, juste beaucoup de tracas. –

5

Les constructeurs sont plus proches des méthodes statiques que des méthodes "normales". Comme les méthodes statiques, ils peuvent être surchargés, mais pas surchargé. Autrement dit, ils ne sont pas hérités mais peuvent être redéfinis.

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 

Constructors et méthodes statiques sont dynamiquement jamais distribué (virtuellement) - Vous savez toujours le type de béton instanciation ou la classe concrète de la méthode statique.C'est pourquoi il n'a aucun sens d'avoir constructeur abstrait et méthode statique abstraite. C'est pourquoi vous ne pouvez pas non plus spécifier constructeur et méthode statique dans interfaces.

Vous pouvez même penser constructeur comme méthode usine statique (voir la corresponding pattern):

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

Le seul cas que je vois où il serait logique de définir constructeur abstrait ou méthode statique abstraite serait si la réflexion est utilisée. Dans ce cas, vous pouvez vous assurer que toute sous-classe redéfinira la méthode ou le constructeur statique correspondant. Mais la réflexion est un autre sujet ...

Note: dans les langages tels que Smalltalk où les classes sont des objets normaux, vous pouvez remplacer la méthode statique et avoir un constructeur abstrait. Mais cela ne s'applique pas à Java car les classes ne sont pas des objets "réguliers" même si vous pouvez les obtenir avec la réflexion.

+0

+1 pour le cas d'utilisation de réflexion. – fre0n