603 votes

Interface définissant une signature de constructeur?

Il est bizarre que ce soit la première fois que je me heurte à ce problème, mais:

Comment définissez-vous un constructeur dans une interface C#?

Éditer
Certains voulaient un exemple (c'est un projet de temps libre, donc oui, c'est un jeu)

IDrawable
+Update
+Draw

Pour pouvoir se mettre à jour (vérifier les bords de l'écran, etc.) et se dessiner, il aura toujours besoin d'un GraphicsDeviceManager. Je veux donc m'assurer que l'objet a une référence à celui-ci. Cela devrait être dans le constructeur.

Maintenant que j'ai écrit cela, je pense que ce que j'implémente ici est IObservable et le GraphicsDeviceManager devrait prendre le IDrawable... Il semble soit que je ne comprenne pas le framework XNA, soit que le framework ne soit pas très bien pensé.

Éditer
Il semble y avoir une certaine confusion concernant ma définition de constructeur dans le contexte d'une interface. Une interface ne peut en effet pas être instanciée et n'a donc pas besoin d'un constructeur. Ce que je voulais définir était en fait une signature de constructeur. Exactement comme une interface peut définir la signature d'une méthode spécifique, l'interface pourrait définir la signature d'un constructeur.

3 votes

Au lieu d'avoir une interface définissant votre constructeur, ayez plutôt une interface définissant vos méthodes de fabrique à la place.

329voto

Jon Skeet Points 692016

Vous ne pouvez pas. C'est parfois une douleur, mais de toute façon vous ne seriez pas en mesure de l'appeler en utilisant des techniques normales.

Dans un article de blog, j'ai suggéré interfaces statiques qui ne pourraient être utilisées que dans des contraintes de types génériques - mais pourraient être vraiment utiles, à mon avis.

Un point à propos si vous pouviez définir un constructeur dans une interface, vous auriez des difficultés à dériver des classes:

public class Foo : IParameterlessConstructor
{
    public Foo() // Selon l'interface
    {
    }
}

public class Bar : Foo
{
    // Aïe! Nous n'avons plus de constructeur sans paramètre...
    public Bar(int x)
    {
    }
}

5 votes

Je pourrais en effet voir des problèmes, mais il en va de même pour toutes les autres méthodes que vous définissez. Habituellement, NotSupportedException est le seul moyen de sortir.

6 votes

@boris: La différence est qu'il y a toujours quelque chose à appeler avec l'héritage normal, garanti par le compilateur. Dans ce cas, il y a quelque chose qui "devrait" être là mais qui ne l'est pas.

1 votes

Si le type A hérite du type B qui à son tour implémente l'interface I, est-ce que A ne fournit pas les propriétés et méthodes de I ? De votre phrase précédente, je déduis que non, mais je ne peux pas trouver un contre-exemple.

146voto

Gert Arnold Points 27642

Une contribution très tardive démontrant un autre problème avec les constructeurs interférés. (J'ai choisi cette question car elle a la meilleure articulation du problème). Supposons que nous puissions avoir:

interface IPerson
{
    IPerson(string name);
}

interface ICustomer
{
    ICustomer(DateTime registrationDate);
}

class Person : IPerson, ICustomer
{
    Person(string name) { }
    Person(DateTime registrationDate) { }
}

Où, par convention, l'implémentation du "constructeur d'interface" est remplacée par le nom du type.

Maintenant, créons une instance:

ICustomer a = new Person("Ernie");

Pouvons-nous dire que le contrat ICustomer est respecté?

Et qu'en est-il de ceci:

interface ICustomer
{
    ICustomer(string address);
}

21 votes

Si vous avez une méthode de même signature et de même nom dans ICustomer et IPerson, ce problème est également là. Je ne vois pas en quoi cela aide. Les interfaces ne sont pas destinées à "ma définition uniquement". Elles sont destinées à "incluez-moi à tout prix"

7 votes

@nawfal Le point est qu'une interface ne demande jamais qu'une méthode soit exécutée, seulement qu'elle existe. Elle ne peut jamais garantir l'état. Au contraire, une "interface de constructeur" exige que quelque chose soit fait (exécuté) lorsqu'un objet est construit. Cela ne peut jamais être garanti lorsqu'il y a différentes interfaces.

9 votes

@GertArnold une méthode fait son travail, alors qu'un constructeur fait le sien. Je ne sais pas comprendre quelle est la différence ici. Les interfaces établissent un contrat selon lequel "mon implémentation doit être là", pas comme "la mienne doit être la seule implémentation". Je dirais que pour des raisons de cohérence, cela devrait être valide pour les constructeurs, les méthodes, les propriétés.

65voto

Michael Points 34110

Vous ne pouvez pas.

Les interfaces définissent des contrats que d'autres objets implémentent et n'ont donc pas d'état à initialiser.

Si vous avez un état à initialiser, vous devriez envisager d'utiliser une classe de base abstraite à la place.

13 votes

Pourquoi un contrat ne peut-il pas avoir un état?

15 votes

Parce que le contrat vous oblige à fournir un certain comportement. Comment les interfaces sont utilisées implique l'extraction de comportements communs, et cela ne dépend pas de l'état (qui serait alors un détail d'implémentation).

1 votes

Il semble que nous pourrions utiliser un mécanisme séparé comme un "contrat" en dehors des interfaces pour exiger qu'un objet implémente certaines fonctionnalités telles que des constructeurs et des méthodes/propriétés statiques qui ne seront pas appelés à travers le contrat (comme si c'était une interface). Suggestion de fonctionnalité pour la prochaine version de .Net ?

21voto

Jeroen Landheer Points 3346

J'ai relu cette question et je me suis dit, peut-être que nous abordons ce problème de la mauvaise manière. Les interfaces pourraient ne pas être la bonne solution lorsqu'il s'agit de définir un constructeur avec certains paramètres... mais une classe de base (abstraite) l'est.

Si vous créez une classe de base avec un constructeur qui accepte les paramètres dont vous avez besoin, chaque classe qui en hérite doit les fournir.

public abstract class Foo
{
  protected Foo(SomeParameter x)
  {
    this.X = x;
  }

  public SomeParameter X { get; private set }
}

public class Bar : Foo // Bar hérite de Foo
{
  public Bar() 
    : base(new SomeParameter("etc...")) // Bar devra fournir le paramètre du constructeur
  {
  }
}

0 votes

Voici comment j'ai également résolu ce problème. Mon interface définit ce que la classe doit être capable de faire, mais ma classe abstraite de base impose le composant du constructeur.

20voto

Jeroen Landheer Points 3346

Il n'est pas possible de créer une interface qui définit des constructeurs, mais il est possible de définir une interface qui force un type à avoir un constructeur sans paramètre, bien que ce soit une syntaxe très laide qui utilise des génériques... Je ne suis pas vraiment sûr que ce soit un bon modèle de codage.

public interface IFoo where T : new()
{
  void SomeMethod();
}

public class Foo : IFoo
{
  // Ceci ne compilera pas
  public Foo(int x)
  {

  }

  #region ITest Members

  public void SomeMethod()
  {
    throw new NotImplementedException();
  }

  #endregion
}

D'autre part, si vous voulez tester si un type a un constructeur sans paramètre, vous pouvez le faire en utilisant la réflexion :

public static class TypeHelper
{
  public static bool HasParameterlessConstructor(Object o)
  {
    return HasParameterlessConstructor(o.GetType());
  }

  public static bool HasParameterlessConstructor(Type t)
  {
    // Utilisation: HasParameterlessConstructor(typeof(SomeType))
    return t.GetConstructor(new Type[0]) != null;
  }
}

J'espère que cela vous aidera.

1 votes

Je vais utiliser le constructeur d'interface pour m'assurer que certains arguments sont définitivement définis (à travers le constructeur) donc un constructeur sans paramètre n'est pas vraiment ce que je recherche.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X