web-dev-qa-db-fra.com

Pourquoi C # n'autorise-t-il pas les méthodes statiques à implémenter une interface?

Pourquoi C # a-t-il été conçu de cette façon?

Si je comprends bien, une interface décrit uniquement le comportement et sert à décrire une obligation contractuelle pour les classes implémentant l'interface que certains comportements sont implémentés.

Si les classes souhaitent implémenter ce comportement dans une méthode partagée, pourquoi ne le devraient-elles pas?

Voici un exemple de ce que j'ai en tête:

// These items will be displayed in a list on the screen.
public interface IListItem {
  string ScreenName();
  ...
}

public class Animal: IListItem {
    // All animals will be called "Animal".
    public static string ScreenName() {
        return "Animal";
    }
....
}

public class Person: IListItem {

    private string name;

    // All persons will be called by their individual names.
    public string ScreenName() {
        return name;
    }

    ....

 }
423
Kramii

En supposant que vous demandez pourquoi vous ne pouvez pas faire cela:

public interface IFoo {
    void Bar();
}

public class Foo: IFoo {
    public static void Bar() {}
}

Cela n'a pas de sens pour moi sémantiquement. Les méthodes spécifiées sur une interface doivent être là pour spécifier le contrat d'interaction avec un objet. Les méthodes statiques ne vous permettent pas d'interagir avec un objet - si vous vous trouvez dans la position où votre implémentation pourrait être rendue statique, vous devrez peut-être vous demander si cette méthode appartient vraiment à l'interface .


Pour implémenter votre exemple, je donnerais à Animal une propriété const qui lui permettrait toujours d’être accessible à partir d’un contexte statique et renverrait cette valeur dans l’implémentation.

public class Animal: IListItem {
    /* Can be tough to come up with a different, yet meaningful name!
     * A different casing convention, like Java has, would help here.
     */
    public const string AnimalScreenName = "Animal";
    public string ScreenName(){ return AnimalScreenName; }
}

Pour une situation plus compliquée, vous pouvez toujours déclarer une autre méthode statique et déléguer à celle-ci. En essayant de trouver un exemple, je ne pouvais imaginer la moindre raison pour laquelle vous feriez quelque chose de non trivial dans un contexte à la fois statique et d'instance. Je vous épargne donc un blob FooBar et le prends comme une indication du fait qu'il pourrait ne pas être une bonne idée.

209

Ma raison technique (simplifiée) est que les méthodes statiques ne sont pas dans la vtable , et le site d'appel est choisi lors de la compilation. C'est la même raison pour laquelle vous ne pouvez pas avoir de membres statiques de substitution ou virtuels. Pour plus de détails, vous aurez besoin d'un diplômé CS ou d'un compilateur wonk - dont je ne suis ni l'un ni l'autre.

Pour des raisons politiques, je vais { cite Eric Lippert (compilateur Wonk et titulaire d'un baccalauréat en mathématiques, informatique et mathématiques appliquées de l'Université de Waterloo (source: LinkedIn } ): 

... le principe de base des méthodes statiques, le principe qui leur donne leur nom ... [est] ... il est toujours possible de déterminer exactement, au moment de la compilation, quelle méthode sera appelée. C'est-à-dire que la méthode peut être résolue uniquement par une analyse statique du code.

Notez que Lippert laisse de la place pour une méthode dite de type:

C’est-à-dire qu’une méthode associée à un type (comme une statique) ne prend pas un argument non nullable «this» (contrairement à une instance ou à un virtuel), mais que la méthode appelée dépend du type construit de T ( contrairement à un statique, qui doit pouvoir être déterminé au moment de la compilation).

mais doit encore être convaincu de son utilité.

161
Mark Brackett

La plupart des réponses ici semblent passer à côté de l'essentiel. Le polymorphisme peut être utilisé non seulement entre les instances, mais également entre les types. Cela est souvent nécessaire lorsque nous utilisons des génériques.

Supposons que nous ayons un paramètre de type dans la méthode générique et que nous devions effectuer certaines opérations avec elle. Nous ne voulons pas créer d’instantané, car nous ne connaissons pas les constructeurs. 

Par exemple:

Repository GetRepository<T>()
{
  //need to call T.IsQueryable, but can't!!!
  //need to call T.RowCount
  //need to call T.DoSomeStaticMath(int param)
}

...
var r = GetRepository<Customer>()

Malheureusement, je ne peux proposer que des alternatives "laides":

  • Utilisez la réflexion Laide et bat l’idée des interfaces et du polymorphisme.

  • Créer une classe d'usine complètement séparée

    Cela pourrait augmenter considérablement la complexité du code. Par exemple, si nous essayons de modéliser des objets de domaine, chaque objet aurait besoin d'une autre classe de référentiel.

  • Instanciez puis appelez la méthode d'interface souhaitée

    Cela peut être difficile à mettre en œuvre même si nous contrôlons la source des classes, utilisées comme paramètres génériques. La raison en est que, par exemple, nous pourrions avoir besoin que les instances ne soient que dans un état connu, "connecté à la base de données". 

Exemple:

public class Customer 
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  void SomeOtherMethod() 
  { 
    //do work...
  }
}

pour utiliser l’instantination pour résoudre le problème de l’interface statique, nous devons procéder comme suit:

public class Customer: IDoSomeStaticMath
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  //dummy instance
  public Customer() { IsDummy = true; }

  int DoSomeStaticMath(int a) { }

  void SomeOtherMethod() 
  { 
    if(!IsDummy) 
    {
      //do work...
    }
  }
}

Ceci est évidemment moche et inutile, ce qui complique le code pour toutes les autres méthodes. De toute évidence, pas une solution élégante non plus!

86
Ivan Arjentinski

Je sais que c'est une vieille question, mais c'est intéressant. L'exemple n'est pas le meilleur. Je pense que ce serait beaucoup plus clair si vous montriez un cas d'utilisation:

 string DoSomething <T> () où T: ISomeFunction 
 {
 if (T.someFunction ()) 
 ...
} 

Être simplement capable d'avoir des méthodes statiques mettre en place une interface ne réaliserait pas ce que vous voulez; ce qui serait nécessaire serait d'avoir des membres statiques comme partie d'une interface. Je peux certainement imaginer de nombreux cas d'utilisation pour cela, notamment lorsqu'il est possible de créer des choses. Deux approches que je pourrais proposer et qui pourraient être utiles:

  1. Créez une classe générique statique dont le paramètre type correspond au type que vous souhaitez transmettre à DoSomething ci-dessus. Chaque variante de cette classe aura un ou plusieurs membres statiques contenant des éléments liés à ce type. Ces informations peuvent être fournies en appelant une routine "information de registre" par chaque classe d'intérêt ou en utilisant Reflection pour obtenir les informations lors de l'exécution du constructeur statique de la variante de classe. Je crois que cette dernière approche est utilisée par des choses comme Comparer <T> .Default () .
  2. Pour chaque classe T d’intérêt, définissez une classe ou une structure qui implémente IGetWurtherClassInfo <T> et qui satisfait à une "nouvelle" contrainte. La classe ne contiendra pas réellement de champs, mais aura une propriété statique qui retourne un champ statique avec les informations de type. Transmettez le type de cette classe ou structure à la routine générique en question, qui pourra créer une instance et l'utiliser pour obtenir des informations sur l'autre classe. Si vous utilisez une classe à cette fin, vous devriez probablement définir une classe générique statique, comme indiqué ci-dessus, afin d'éviter de devoir créer chaque fois une nouvelle instance descripteur-objet. Si vous utilisez une structure, le coût d'instanciation devrait être nul, mais chaque type de structure nécessiterait un développement différent de la routine DoSomething .

Aucune de ces approches n'est vraiment attrayante. D'autre part, je m'attendrais à ce que si les mécanismes existants dans CLR fournissaient ce type de fonctionnalité proprement, .net permettrait de spécifier des "nouvelles" contraintes paramétrées (puisque savoir si une classe a un constructeur doté être difficilement comparable à savoir s’il existe une méthode statique avec une signature particulière).

18
supercat

Les interfaces spécifient le comportement d'un objet.

Les méthodes statiques ne spécifient pas le comportement d'un objet, mais un comportement qui affecte un objet d'une manière ou d'une autre.

14
John Kraft

Dans la mesure où les interfaces représentent des "contrats", il semble assez raisonnable que les classes statiques implémentent des interfaces. 

Les arguments ci-dessus semblent tous passer à côté de ce point concernant les contrats.

13
George

La myopie, je suppose.

Lors de la conception initiale, les interfaces étaient uniquement destinées à être utilisées avec des instances de classe.

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

Ce n’est que lors de l’introduction d’interfaces que les contraintes imposées aux génériques ont été d’ajouter une méthode statique à une interface.

(répondant au commentaire :) Je pense que le changer maintenant nécessiterait une modification du CLR, ce qui entraînerait des incompatibilités avec les assemblées existantes.

13
James Curran

Comme le but d'une interface est de permettre le polymorphisme, être capable de passer une instance d'un nombre quelconque de classes définies qui ont toutes été définies pour implémenter l'interface définie ... garantissant que dans votre appel polymorphe, le code sera capable de trouver la méthode que vous appelez. cela n'a aucun sens de permettre à une méthode statique d'implémenter l'interface, 

Comment l'appelleriez-vous ?? 


public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
    public static void MyMethod() { //Do Something; }
}

 // inside of some other class ...  
 // How would you call the method on the interface ???
    MyClass.MyMethod();  // this calls the method normally 
                         // not through the interface...

    // This next fails you can't cast a classname to a different type... 
    // Only instances can be Cast to a different type...
    MyInterface myItf = MyClass as MyInterface;  
9
Charles Bretana

En ce qui concerne les méthodes statiques utilisées dans des contextes non génériques, je conviens qu'il n'est pas très logique de les autoriser dans des interfaces, car vous ne pourriez pas les appeler si vous aviez quand même une référence à l'interface. Cependant, il existe un vide fondamental dans la conception du langage créé en utilisant des interfaces PAS dans un contexte polymorphe, mais dans un contexte générique. Dans ce cas, l'interface n'est pas du tout une interface, mais plutôt une contrainte. Parce que C # n'a pas de concept de contrainte en dehors d'une interface, il manque des fonctionnalités substantielles. Exemple:

T SumElements<T>(T initVal, T[] values)
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

Ici, il n'y a pas de polymorphisme, le générique utilise le type réel de l'objet et appelle l'opérateur + =, mais cela échoue car il ne peut pas affirmer avec certitude que cet opérateur existe. La solution simple consiste à le spécifier dans la contrainte; la solution simple est impossible car les opérateurs sont statiques et les méthodes statiques ne peuvent pas être dans une interface et (voici le problème) les contraintes sont représentées sous forme d'interfaces.

Ce dont C # a besoin est un type de contrainte réel, toutes les interfaces seraient également des contraintes, mais toutes les contraintes ne seraient pas des interfaces, alors vous pouvez le faire:

constraint CHasPlusEquals
{
    static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}

T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

On a déjà beaucoup parlé de faire une arithmétique pour tous les types numériques à implémenter, mais on s'interroge sur l'efficacité, étant donné qu'une contrainte n'est pas une construction polymorphe, une contrainte CArithmétique résoudrait ce problème.

4
Jeremy Sorensen

Ce que vous semblez vouloir permettre à une méthode statique d’être appelée à la fois par le type ou par une instance de ce type. Cela créerait au moins une ambiguïté qui n’est pas un trait souhaitable.

Il y aurait des débats sans fin pour savoir si cela importait, quelle était la meilleure pratique et s'il existait des problèmes de performances d'une manière ou d'une autre. En ne le supportant pas, C # nous évite de nous en inquiéter.

Il est également probable qu'un compilateur qui se conformerait à ce désir perdrait certaines optimisations pouvant entraîner une séparation plus stricte entre les méthodes instance et statique.

3
AnthonyWJones

Vous pouvez considérer les méthodes statiques et les méthodes non statiques d'une classe comme des interfaces différentes. Lorsqu'elles sont appelées, les méthodes statiques se résolvent en objet de classe singleton, et les méthodes non statiques, en instance de la classe à laquelle vous faites face. Ainsi, si vous utilisez des méthodes statiques et non statiques dans une interface, vous déclareriez effectivement deux interfaces alors que nous voulons réellement que les interfaces soient utilisées pour accéder à un élément cohésif.

3
Scott Langham

Pour donner un exemple où il me manque une implémentation statique de méthodes d'interface ou ce que Mark Brackett a présenté comme "méthode dite de type":

Lors de la lecture d'un stockage de base de données, nous avons une classe DataTable générique qui gère la lecture d'une table de n'importe quelle structure. Toutes les informations spécifiques à la table sont placées dans une classe par table. Elle contient également les données d'une ligne de la base de données et doit implémenter une interface IDataRow. IDataRow contient une description de la structure de la table à lire dans la base de données. Le DataTable doit demander la structure de données à IDataRow avant d'être lu à partir de la base de données. Actuellement cela ressemble à:

interface IDataRow {
  string GetDataSTructre();  // How to read data from the DB
  void Read(IDBDataRow);     // How to populate this datarow from DB data
}

public class DataTable<T> : List<T> where T : IDataRow {

  public string GetDataStructure()
    // Desired: Static or Type method:
    // return (T.GetDataStructure());
    // Required: Instantiate a new class:
    return (new T().GetDataStructure());
  }

}

GetDataStructure n'est requis qu'une seule fois par chaque table à lire, la surcharge liée à l'instanciation d'une instance supplémentaire est minimale. Cependant, ce serait bien dans ce cas ici.

3
Jesper Grooss

Les interfaces étant dans une structure d'héritage, les méthodes statiques n'héritent pas bien.

3
Joel Coehoorn

La plupart des gens semblent oublier que, dans OOP, les classes sont également des objets. Elles ont donc des messages qui, pour une raison quelconque, sont appelés "méthode statique" . défauts ou faiblesses de la langue . Optimiste à propos de c # quoique ...

1
Mar Bar

Les interfaces sont des ensembles abstraits de fonctionnalités disponibles définies.

Le fait qu'une méthode de cette interface se comporte ou non de manière statique est un détail d'implémentation qui doit être caché derrière l'interface. Il serait erroné de définir une méthode d'interface comme statique car vous obligeriez inutilement à implémenter la méthode d'une certaine manière.

Si les méthodes étaient définies comme statiques, la classe implémentant l'interface ne serait pas aussi encapsulée que possible. L'encapsulation est une bonne chose à faire dans la conception orientée objet (je ne vais pas entrer dans pourquoi, vous pouvez le lire ici: http://en.wikipedia.org/wiki/Object-oriented ) Pour cette raison, les méthodes statiques ne sont pas autorisées dans les interfaces.

1
Scott Langham

Les classes statiques doivent pouvoir le faire pour pouvoir être utilisées de manière générique. J'ai dû mettre en place un Singleton pour obtenir les résultats souhaités.

J'avais un tas de classes de la couche de gestion statique qui implémentaient des méthodes CRUD comme "Créer", "Lire", "Mettre à jour", "Supprimer" pour chaque type d'entité comme "Utilisateur", "Équipe", etc. contrôle qui avait une propriété abstraite pour la classe de couche de gestion qui implémentait les méthodes CRUD. Cela m'a permis d'automatiser les opérations "Créer", "Lire", "Mettre à jour", "Supprimer" de la classe de base. J'ai dû utiliser un Singleton à cause de la limitation statique.

1
Louis Rebolloso

C # et le CLR doivent prendre en charge les méthodes statiques dans les interfaces, comme le fait Java. Le modificateur statique fait partie d'une définition de contrat et a une signification, en particulier le fait que le comportement et la valeur de retour ne varient pas d'une instance à l'autre bien qu'ils puissent toujours varier d'un appel à l'autre.

Cela dit, je vous recommande d'utiliser une annotation lorsque vous souhaitez utiliser une méthode statique dans une interface. Vous obtiendrez la fonctionnalité que vous recherchez.

1
Daniel Barbalace

FYI: Vous pouvez obtenir un comportement similaire à celui que vous voulez en créant des méthodes d'extension pour l'interface. La méthode d'extension serait un comportement statique partagé, non remplaçable. Malheureusement, cette méthode statique ne ferait pas partie du contrat.

1
Daniel Auger

Le fait qu'une classe statique soit implémentée en C # par Microsoft, créant ainsi une instance spéciale d'une classe avec les éléments statiques, n'est qu'une bizarrerie de la manière dont la fonctionnalité statique est réalisée. Ce n'est pas un point théorique.

Une interface DEVRAIT être un descripteur de l'interface de classe - ou de la façon dont elle est interagie, et qui devrait inclure des interactions statiques. La définition générale d'interface (de Meriam-Webster): l'endroit ou la zone où différentes choses se rencontrent et communiquent ou se touchent. Lorsque vous omettez complètement les composants statiques d'une classe ou des classes statiques, nous ignorons de grandes sections de la manière dont ces mauvais garçons interagissent.

Voici un exemple très clair de la possibilité d'utiliser des interfaces avec des classes statiques: 

public interface ICrudModel<T, Tk>
{
    Boolean Create(T obj);
    T Retrieve(Tk key);
    Boolean Update(T obj);
    Boolean Delete(T obj);
}

Actuellement, j'écris les classes statiques qui contiennent ces méthodes sans aucune sorte de vérification pour m'assurer que je n'ai rien oublié. Est comme le mauvais vieux temps de la programmation avant la POO.

1
Thomas Phaneuf

Je pense que la réponse courte est "parce que son utilité est nulle". Pour appeler une méthode d'interface, vous avez besoin d'une instance du type. À partir de méthodes d'instance, vous pouvez appeler les méthodes statiques souhaitées.

0
mackenir

ne faites pas cela, essayez plutôt des classes abstraites

public abstract class ExampleBase
{
    /// <summary>
    /// Do it
    /// </summary>
    public virtual abstract static void DoIt();
}
0
Eric Silveira

Conceptuellement il n'y a aucune raison pour laquelle une interface ne pourrait pas définir un contrat incluant des méthodes statiques.

Pour l'implémentation actuelle du langage C #, la restriction est due à la tolérance d'héritage d'une classe de base et d'interfaces. Si "class SomeBaseClass" implémente "interface ISomeInterface" et "class SomeDerivedClass: SomeBaseClass, ISomeInterface" implémente également l'interface, une méthode statique pour implémenter une méthode d'interface échouera à la compilation être présent dans la classe de base pour implémenter l'interface).

Une classe statique est fonctionnellement identique à un singleton et remplit les mêmes fonctions qu'un singleton avec une syntaxe plus propre. Puisqu'un singleton peut implémenter une interface, les implémentations d'interface par la statique sont conceptuellement valables.

Cela revient donc simplement à la limitation du conflit de noms C #, par exemple, et aux méthodes statiques du même nom sur l'ensemble de l'héritage. Il n'y a aucune raison pour que C # ne puisse pas être "mis à niveau" pour prendre en charge les contrats de méthodes statiques (interfaces).

0
Greg McPherran

OK voici un exemple de besoin d'une "méthode type". Je suis en train de créer une classe parmi un ensemble de classes basées sur du XML source. Donc j'ai un 

  static public bool IsHandled(XElement xml)

fonction qui est appelée à tour de rôle sur chaque classe.

La fonction devrait être statique, sinon nous perdrions du temps à créer des objets inappropriés . Comme le fait remarquer @Ian Boyde, cela pourrait être fait dans une classe d'usine, mais cela ne fait qu'ajouter à la complexité.

Ce serait bien de l'ajouter à l'interface pour forcer les développeurs de classe à l'implémenter. Cela ne causerait pas de surcharge significative - il ne s'agit que d'une vérification du temps de compilation/liaison et n'affecte pas la table virtuelle.

Cependant, ce serait aussi une amélioration assez mineure. Comme la méthode est statique, en tant qu'appelant, je dois l'appeler explicitement et obtenir une erreur de compilation immédiate si elle n'est pas implémentée. Le fait de le spécifier sur l'interface signifierait que cette erreur survient de manière marginale plus tôt dans le cycle de développement, mais ceci est trivial par rapport à d'autres problèmes d'interface endommagée.

Il s’agit donc d’une caractéristique potentielle mineure qui, dans l’ensemble, devrait probablement être laissée de côté.

0
Stephen Westlake

Lorsqu'une classe implémente une interface, elle crée une instance pour les membres de l'interface. Bien qu'un type statique ne possède pas d'instance, il ne sert à rien d'avoir des signatures statiques dans une interface.

0
Vinay Chanumolu

Selon le concept orienté objet Interface implémentée par les classes et avoir un contrat pour accéder à ces fonctions (ou méthodes) implémentées en utilisant objet.

Donc, si vous voulez accéder aux méthodes de contrat d'interface, vous devez créer un objet. C'est toujours ce qui n'est pas permis dans le cas de méthodes statiques. Les classes statiques, les méthodes et les variables ne nécessitent jamais d'objets et ne se chargent pas en mémoire sans créer d'objet de cette zone (ou classe) ou vous pouvez dire ne pas nécessiter de création d'objet.

0
Sanjeev Saraswat

Je pense que la question concerne le fait que C # a besoin d'un autre mot clé, précisément pour ce type de situation. Vous voulez une méthode dont la valeur de retour dépend uniquement du type sur lequel elle est appelée. Vous ne pouvez pas l'appeler "statique" si le type en question est inconnu. Mais une fois que le type sera connu, il deviendra statique. L'idée est "statique non résolue" - ce n'est pas encore statique, mais une fois que nous connaîtrons le type de destinataire, ce le sera. C'est un très bon concept, c'est pourquoi les programmeurs ne cessent de le demander. Mais cela ne correspond pas tout à fait à la façon dont les concepteurs ont envisagé le langage.

Comme il n’est pas disponible, j’ai commencé à utiliser des méthodes non statiques comme indiqué ci-dessous. Pas vraiment idéal, mais je ne vois pas d’approche plus sensée, du moins pas pour moi.

public interface IZeroWrapper<TNumber> {
  TNumber Zero {get;}
}

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}
0
William Jockusch