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


447

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

Si je comprends bien, une interface ne décrit que le comportement, et sert à décrire une obligation contractuelle pour les classes implémentant l'interface que certains comportements sont mis en œuvre.

Si les classes souhaitent implémenter ce comportement dans une méthode partagée, pourquoi 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;
    }

    ....

 }

6
Eh bien, Java 8 l'a ( stackoverflow.com/questions/23148471/… ).
liang

1
Découvrez comment combiner un comportement statique avec l'héritage ou la mise en œuvre d'une interface: stackoverflow.com/a/13567309/880990
Olivier Jacot-Descombes

1
IListItem.ScreenName() => ScreenName()(en utilisant la syntaxe C # 7) implémentera explicitement la méthode d'interface en appelant la méthode statique. Les choses tournent mal quand vous ajoutez l'héritage à cela, cependant (vous devez réimplémenter l'interface)
Jeroen Mostert

2
Faire savoir à tout le monde que l'attente est maintenant terminée! C # 8.0 a des méthodes d'interface statiques: dotnetfiddle.net/Lrzy6y (bien qu'elles fonctionnent un peu différemment de la façon dont OP voulait qu'elles fonctionnent - vous n'avez pas à les implémenter)
Jamie Twells

Réponses:


221

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 retournerait 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 y déléguer. En essayant de trouver un exemple, je ne pouvais penser à aucune raison pour laquelle vous feriez quelque chose de non trivial à la fois dans un contexte statique et d'instance, donc je vous épargnerai un blob FooBar, et le considérerai comme une indication qu'il pourrait pas une bonne idée.


6
Un parfait exemple! Mais je ne suis pas sûr de comprendre votre raisonnement. Le compilateur aurait sûrement pu être conçu pour examiner également les membres statuc? Les instances n'ont-elles pas de table d'adresses pour l'implémentation de ces méthodes? Les méthodes statiques n'ont-elles pas pu être incluses dans ce tableau?
Kramii

12
Il y a un cas où cela pourrait être utile. Par exemple, je veux que tous les implémenteurs implémentent une méthode GetInstance qui prend un argument XElement. Je ne peux ni définir cela comme une méthode statique dans l'interface, ni exiger une signature de constructeur de l'interface.
oleks

25
Beaucoup de gens ont pesé des deux côtés: de "Cela n'a aucun sens" à "C'est un bug, j'aimerais que vous le puissiez." (Je pense qu'il existe des cas d'utilisation valides pour cela, c'est ainsi que je me suis retrouvé ici.) Comme solution de contournement, la méthode d'instance peut simplement déléguer à une méthode statique.
harpo

5
Vous pouvez également implémenter simplement la pièce comme méthode d'extension sur l'interface de base, comme dans: public static object MethodName(this IBaseClass base)au sein d'une classe statique. L'inconvénient, cependant, est que contrairement à un héritage d'interface - cela ne force pas / ne permet pas aux héritiers individuels de bien remplacer la méthodologie.
Troy Alford

8
Cela aurait beaucoup de sens avec les génériques. Par exemple, annulez Something <T> () où T: ISomeInterface {new T (). DoSomething1 (); T.DoSomething2 (); }
Francisco Ryan Tolmasky I

173

Ma raison technique (simplifiée) est que les méthodes statiques ne sont pas dans le vtable , et le site d'appel est choisi au moment de la compilation. C'est la même raison pour laquelle vous ne pouvez pas avoir de remplacement ou de membres statiques virtuels. Pour plus de détails, vous auriez besoin d'un won CS grad ou compilateur - dont je ne suis pas un.

Pour des raisons politiques, je citerai Eric Lippert (qui est un gagnant de compilateur 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 de la conception des méthodes statiques, le principe qui leur donne leur nom ... [est] ... on peut toujours déterminer exactement, au moment de la compilation, quelle méthode sera appelée. Autrement dit, la méthode peut être résolue uniquement par une analyse statique du code.

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

C'est-à-dire, une méthode associée à un type (comme un statique), qui ne prend pas un argument «this» non nul (contrairement à une instance ou virtuelle), mais un où la méthode appelée dépendrait du type construit de T ( contrairement à un statique, qui doit être déterminable au moment de la compilation).

mais doit encore être convaincu de son utilité.


5
Excellent, c'est la réponse que je voulais écrire - je ne connaissais tout simplement pas le détail de l'implémentation.
Chris Marasti-Georg

5
Très bonne réponse. Et je veux cette «méthode de type»! Serait utile dans de nombreuses occasions (pensez aux métadonnées pour un type / classe).
Philip Daubmeier

23
Ceci est la bonne réponse. Vous passez une interface à quelqu'un, il a besoin de savoir comment appeler une méthode. Une interface n'est qu'une table de méthodes virtuelle . Votre classe statique n'a pas ça. L'appelant ne sais pas comment appeler une méthode. (Avant de lire cette réponse je pensais que C # venait juste d' être pédant. Maintenant , je sais qu'il est une limitation technique, imposée par ce une interface est ). D'autres personnes vous parleront de la mauvaise conception. Ce n'est pas une mauvaise conception - c'est une limitation technique.
Ian Boyd

2
+1 pour avoir répondu à la question et ne pas être pédant et arrogant. Comme Ian Boyd l'a dit: "Ce n'est pas une mauvaise conception - c'est une limitation technique."
Joshua Pech

3
Il est totalement possible de générer un objet pour une classe statique avec vtable associé. Regardez comment Scala gère les objects et comment ils sont autorisés à implémenter des interfaces.
Sebastian Graf

97

La plupart des réponses semblent manquer ici. 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 ayons besoin de faire une opération avec. Nous ne voulons pas instancier, car nous ignorons 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 Ugly 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.

  • Instancier puis appeler la méthode d'interface souhaitée

    Cela peut être difficile à implémenter 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 soient uniquement dans un état "connecté à la base de données" bien connu.

Exemple:

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

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

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

afin d'utiliser l'instanciation pour résoudre le problème d'interface statique, nous devons faire la chose suivante:

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

C'est évidemment laid et complique également le code pour toutes les autres méthodes. Évidemment, ce n'est pas non plus une solution élégante!


35
+1 pour "La plupart des réponses semblent manquer ici."; c'est incroyable comme il semble que presque toutes les réponses esquivent le cœur de la question pour se plonger dans un verbiage pour la plupart inutile ...

5
@Chris Voici un exemple spécifique qui m'a fait frapper à nouveau cette restriction. Je veux ajouter une interface IResetting aux classes pour indiquer qu'elles mettent en cache certaines données dans des variables statiques qui peuvent être réinitialisées par l'administrateur du site (par exemple une liste de catégories de commandes, un ensemble de taux de TVA, une liste de catégories récupérées à partir d'une API externe) pour réduire la DB et les hits d'API externes, cela utiliserait évidemment une réinitialisation de méthode statique. Cela me permet d'automatiser simplement la détection des classes qui peuvent être réinitialisées. Je peux toujours le faire, mais la méthode n'est pas appliquée ou ajoutée automatiquement dans l'IDE et repose sur l'espoir.
mattmanser

6
@ Chris, je ne suis pas d'accord, exagération massive. C'est toujours le signe d'un défaut de langage lorsque plus d'architecture est la «meilleure» solution. Vous vous souvenez de tous les modèles dont personne ne parle plus depuis que C # a obtenu des génériques et des méthodes anonymes?
mattmanser

3
Ne pouvez-vous pas utiliser "où T: IQueryable, T: IDoSomeStaticMath" ou similaire?
Roger Willcocks

2
@ ChrisMarasti-Georg: Une façon un peu sale mais intéressante de contourner cette petite construction:, public abstract class DBObject<T> where T : DBObject<T>, new()puis de faire hériter toutes les classes DB DBObject<T>. Ensuite, vous pouvez faire de la récupération par clé une fonction statique avec le type de retour T dans la superclasse abstraite, faire en sorte que cette fonction crée un nouvel objet de T, puis appeler un objet protégé String GetRetrieveByKeyQuery()(défini comme abstrait sur la superclasse) sur cet objet pour obtenir la requête réelle à exécuter. Bien que cela puisse être un peu hors sujet
Nyerguds

20

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:

chaîne DoSomething <T> () où T: ISomeFunction
{
  if (T.someFunction ())
    ...
}

Le simple fait d'avoir des méthodes statiques implémentant une interface ne permettrait pas d'atteindre ce que vous voulez; ce qui serait nécessaire serait d'avoir des membres statiques dans le cadre d'une interface. Je peux certainement imaginer de nombreux cas d'utilisation pour cela, surtout quand il s'agit de pouvoir créer des choses. Je pourrais proposer deux approches qui pourraient être utiles:

  1. Créez une classe générique statique dont le paramètre type sera le type que vous transmettriez à 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 soit en demandant à chaque classe d'intérêt d'appeler une routine "enregistrer les informations", soit en utilisant Reflection pour obtenir les informations lorsque le constructeur statique de la variation de classe est exécuté. 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 IGetWwhatClassInfo <T> et satisfait une "nouvelle" contrainte. La classe ne contiendra en fait aucun champ, mais aura une propriété statique qui renvoie un champ statique avec les informations de type. Passez 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 à cet effet, vous devez probablement définir une classe générique statique comme indiqué ci-dessus, pour éviter d'avoir à construire une nouvelle instance d'objet descripteur à chaque fois. Si vous utilisez une structure, le coût d'instanciation doit être nul, mais chaque type de structure différent nécessiterait une extension différente de la routine DoSomething.

Aucune de ces approches n'est vraiment attrayante. D'un autre côté, je m'attendrais à ce que si les mécanismes existaient dans CLR pour fournir ce type de fonctionnalité proprement, .net permettrait de spécifier des "nouvelles" contraintes paramétrées (car savoir si une classe a un constructeur avec une signature particulière semblerait être comparable en difficulté à savoir si elle a une méthode statique avec une signature particulière).


16

Myopie, je suppose.

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

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

Ce n'est qu'avec l'introduction des interfaces que les contraintes pour les génériques ont fait que l'ajout d'une méthode statique à une interface avait une utilité pratique.

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


C'est dans le contexte des génériques que j'ai rencontré le problème pour la première fois, mais je me demande si l'inclusion de méthodes statiques dans les interfaces pourrait aussi être utile dans d'autres contextes? Y a-t-il une raison pour laquelle les choses n'ont pas pu être modifiées?
Kramii

moi aussi, je rencontre cela lors de l'implémentation d'une classe générique qui nécessite le type de paramètre pour se créer avec certains paramètres. Puisque le nouveau () ne peut pas en prendre. Avez-vous déjà trouvé comment faire cela, Kramii?
Tom

1
@Kramii: contrats pour les API statiques. Je ne veux pas d'une instance d'objet, juste une garantie d'une signature particulière, par exemple. IMatrixMultiplier ou ICustomSerializer. Les fonctions / actions / délégués en tant que membres de la classe font l'affaire, mais l'OMI cela semble parfois exagéré et peut être déroutant pour les non-expérimentés essayant d'étendre l'API.
David Cuccia

14

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.


71
Désolé .. je ne suis pas sûr que ce soit vrai! Une interface ne spécifie pas de comportement. Une interface définit un ensemble d'opérations nommées. Deux classes pourraient implémenter une méthode d'interface pour se comporter de manières complètement différentes. Ainsi, une interface ne spécifie pas du tout le comportement. La classe qui l'implémente le fait.
Scott Langham

1
J'espère que vous ne pensez pas que je suis difficile… mais je pense que c'est une distinction importante pour quiconque apprend OO à comprendre.
Scott Langham

4
Une interface est censée spécifier un contrat qui inclut le comportement et la présentation. C'est pourquoi changer le comportement d'un appel d'interface est un non-non car les deux sont censés être corrigés. Si vous avez une interface où l'appel agit différemment (c'est-à-dire qu'IList.Add a supprimé), ce ne serait pas correct.
Jeff Yates

14
Eh bien, oui, vous seriez déformé dans la tête pour définir une méthode pour se comporter de manière incongrue avec son nom. Si vous aviez IAlertService.GetAssistance (), son comportement pourrait être de faire clignoter une lumière, de déclencher une alarme ou de piquer dans les yeux avec un bâton.
Scott Langham

1
Une implémentation pourrait également écrire dans un fichier journal. Ce comportement n'est pas spécifié dans l'interface. Mais vous avez peut-être raison. Le comportement pour «obtenir de l'aide» doit vraiment être respecté.
Scott Langham

14

Dans la mesure où les interfaces représentent des "contrats", il semble raisonnable pour les classes statiques d'implémenter des interfaces.

Les arguments ci-dessus semblent tous manquer ce point sur les contrats.


2
Je suis totalement d'accord avec cette réponse simple mais efficace. Ce qui serait intéressant dans une "interface statique", c'est qu'elle représenterait un contrat. Peut-être que cela ne devrait pas être appelé une "interface statique" mais nous manquons encore une construction. Par exemple, consultez le document officiel de .NET sur l'interface ICustomMarshaler. Il requiert la classe qui l'implémente pour "ajouter une méthode statique appelée GetInstance qui accepte une chaîne comme paramètre et a un type de retour ICustomMarshaler". Cela ressemble sérieusement à une définition d '"interface statique" en anglais simple alors que je la préférerais en C # ...
Simon Mourier

@SimonMourier Cette documentation aurait pu être écrite plus clairement, mais vous l'avez mal interprétée. Ce n'est pas l'interface ICustomMarshaler qui nécessite la méthode statique GetInstance. C'est l'attribut de code [MarshalAs] qui l'exige. Ils utilisent le modèle d'usine pour permettre à l'attribut d'obtenir une instance du marshaleur attaché. Malheureusement, ils ont complètement oublié d'inclure la documentation de l'exigence GetInstance sur la page de documentation MarshalAs (il ne montre que des exemples utilisant des implémentations de marshaling intégrées).
Scott Gartner

@ScottGartner - N'obtenez pas votre point. msdn.microsoft.com/en-us/library/… indique clairement: "En plus d'implémenter l'interface ICustomMarshaler, les marshalers personnalisés doivent implémenter une méthode statique appelée GetInstance qui accepte une chaîne comme paramètre et a un type de retour ICustomMarshaler. La méthode statique est appelée par la couche d'interopérabilité COM du Common Language Runtime pour instancier une instance du marshaleur personnalisé. ". Il s'agit définitivement d'une définition de contrat statique.
Simon Mourier

9

Parce que le but d'une interface est de permettre le polymorphisme, de pouvoir passer une instance de n'importe quel nombre de classes définies qui ont toutes été définies pour implémenter l'interface définie ... garantissant qu'au sein de votre appel polymorphe, le code pourra trouver la méthode que vous appelez. cela n'a aucun sens de permettre à une méthode statique de mettre en œuvre l'interface,

Comment appelleriez-vous ça ??


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;  

1
D'autres langages (Java, par exemple) permettent d'appeler des méthodes statiques à partir d'instances d'objets, mais vous obtiendrez un avertissement vous invitant à les appeler à partir d'un contexte statique.
Chris Marasti-Georg

Autoriser une méthode statique à être appelée à partir d'une instance est également autorisé dans .Net. C'est autre chose. Si une méthode statique implémentait une interface, vous pourriez l'appeler SANS instance. C'est ce qui n'a pas de sens. N'oubliez pas que vous ne pouvez pas mettre l'implémentation dans une interface, elle doit être dans la classe. Donc, si cinq classes différentes étaient définies pour implémenter cette interface, et que chacune avait une implémentation différente de cette méthode statique, laquelle utiliserait le compilateur?
Charles Bretana

1
@CharlesBretana dans le cas des génériques, celui du type transmis. Je vois beaucoup d'utilité d'avoir des interfaces pour les méthodes statiques (ou si vous voulez, appelons-les "interfaces statiques" et permettons à une classe de définir les deux interfaces statiques et les interfaces d'instance). Donc, si je l'ai, Whatever<T>() where T:IMyStaticInterfaceje pourrais appeler Whatever<MyClass>()et avoir T.MyStaticMethod()à l'intérieur de l' Whatever<T>()implémentation sans avoir besoin d'une instance. La méthode à appeler serait déterminée lors de l'exécution. Vous pouvez le faire via la réflexion, mais il n'y a pas de "contrat" ​​à forcer.
2015

4

En ce qui concerne les méthodes statiques utilisées dans des contextes non génériques, je suis d'accord que cela n'a pas beaucoup de sens de les autoriser dans les interfaces, puisque vous ne seriez pas en mesure de les appeler si vous aviez une référence à l'interface de toute façon. Cependant, il y a un trou fondamental dans la conception du langage créé en utilisant des interfaces NON 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 lui manque des fonctionnalités substantielles. Exemple concret:

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 dire avec certitude que cet opérateur existe. La solution simple est de 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 comme des interfaces.

Ce dont C # a besoin est un véritable type de contrainte, toutes les interfaces seraient également des contraintes, mais toutes les contraintes ne seraient pas des interfaces, alors vous pourriez faire ceci:

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

Il a déjà été beaucoup question de créer une IArithmétique pour tous les types numériques à implémenter, mais il y a des inquiétudes quant à l'efficacité, car une contrainte n'est pas une construction polymorphe, la création d'une contrainte CArithmétique résoudrait ce problème.


Les opérateurs en C # sont statiques, donc cela n'a toujours pas de sens.
John Saunders

C'est le point, la contrainte vérifie que le TYPE (pas l'instance) a l'opérateur + (et par extension l'opérateur + =) et permet au modèle d'être généré, puis lorsque la substitution de modèle réelle se produit, l'objet utilisé est garanti d'être d'un type qui a cet opérateur (ou une autre méthode statique). Vous pouvez donc dire: SumElements <int> (0, 5); qui instancierait ceci: SumElements (int initVal, int [] values) {foreach (var v in values) {initVal + = v; }}, ce qui, bien sûr, est parfaitement logique
Jeremy Sorensen

1
N'oubliez pas que ce n'est pas un modèle. Ce sont des génériques, qui ne sont pas les mêmes que les modèles C ++.
John Saunders

@JohnSaunders: Les génériques ne sont pas des modèles, et je ne sais pas comment on pourrait raisonnablement les faire fonctionner avec des opérateurs liés statiquement, mais dans des contextes génériques il y a de nombreux cas où il pourrait être utile de spécifier qu'un Tdevrait avoir un statique membre (par exemple, une usine qui produit des Tinstances). Même sans changements d'exécution, je pense qu'il serait possible de définir des conventions et des méthodes d'assistance qui permettraient aux langages d'implémenter une chose telle que le sucre syntaxique de manière efficace et interopérable. Si pour chaque interface avec des méthodes statiques virtuelles, il y avait une classe d'assistance ...
supercat

1
@JohnSaunders: Le problème n'est pas tant que la méthode implémente une interface, mais plutôt que le compilateur ne peut pas sélectionner une méthode virtuelle à appeler sans avoir une instance d'objet sur laquelle baser la sélection. Une solution consiste à envoyer l'appel "interface statique" non pas à l'aide de la répartition virtuelle (qui ne fonctionnera pas) mais à la place en utilisant un appel à une classe statique générique. La répartition générique basée sur le type ne nécessite pas d'avoir une instance, mais simplement un type.
supercat

3

Parce que les interfaces sont dans une structure d'héritage et que les méthodes statiques n'héritent pas bien.


3

Ce que vous semblez vouloir permettre à une méthode statique d'être appelée via le Type ou n'importe quelle instance de ce type. Cela entraînerait au moins une ambiguïté qui n'est pas un trait souhaitable.

Il y aurait des débats interminables pour savoir si cela importait, ce qui est la meilleure pratique et s'il y a des problèmes de performance dans un sens ou dans un autre. En ne le supportant simplement pas, C # nous évite de nous en préoccuper.

Il est également probable qu'un compilateur conforme à ce désir perdrait certaines optimisations qui pourraient s'accompagner d'une séparation plus stricte entre les méthodes d'instance et les méthodes statiques.


Fait intéressant, il est tout à fait possible d'appeler une méthode statique à la fois par type Type Instance dans VB.Net (même si l'EDI donne un avertissement dans ce dernier cas). Cela ne semble pas être un problème. Vous avez peut-être raison sur les optimisations.
Kramii

3

Vous pouvez considérer les méthodes statiques et les méthodes non statiques d'une classe comme étant des interfaces différentes. Lorsqu'elles sont appelées, les méthodes statiques se résolvent en l'objet de classe statique singleton et les méthodes non statiques se résolvent en l'instance de la classe avec laquelle vous traitez. Donc, si vous utilisez des méthodes statiques et non statiques dans une interface, vous déclareriez effectivement deux interfaces alors que nous voulons vraiment que les interfaces soient utilisées pour accéder à une chose cohérente.


C'est un POV intéressant, et c'est probablement celui que les concepteurs C # avaient en tête. Je penserai désormais aux membres statiques différemment.
Kramii

3

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

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

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 pour chaque table à lire, la surcharge pour l'instanciation d'une autre instance est minime. Cependant, ce serait bien dans ce cas ici.


1

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


1

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

Qu'une méthode dans cette interface se comporte ou non comme statique est un détail d'implémentation qui doit être caché derrière l'interface . Il serait faux de définir une méthode d'interface comme statique, car vous forceriez inutilement la méthode à être implémentée 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 qu'elle pourrait l'être. L'encapsulation est une bonne chose à rechercher dans la conception orientée objet (je n'entrerai pas dans le 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
Oui, une statique dans la déclaration d'interface serait idiote. Une classe ne devrait pas être forcée d'implémenter l'interface d'une certaine manière. Cependant, C # limite-t-il une classe à l'implémentation de l'interface à l'aide de méthodes non statiques. Est-ce que ça a du sens? Pourquoi?
Kramii

Vous ne pouvez pas utiliser le mot clé "statique". Il n'y a cependant aucune restriction car vous n'avez pas besoin du mot clé statique pour écrire une méthode qui se comporte statiquement. Écrivez simplement une méthode qui n'accède à aucun des membres de son objet, elle se comportera comme une méthode statique. Il y a donc une restriction.
Scott Langham

C'est vrai Scott, mais cela ne permet pas à quelqu'un d'accéder à cette méthode de manière statique, dans une autre partie du code. Non pas que je puisse penser à une raison que vous voudriez, mais cela semble être ce que le PO demande.
Chris Marasti-Georg

Eh bien, si vous ressentez vraiment le besoin, vous pouvez l'écrire en tant que méthode statique d'accès dans une autre partie du code, et simplement écrire la méthode non statique pour appeler la méthode statique. Je peux me tromper, mais je doute que ce soit un objectif du questionneur.
Scott Langham

1

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

J'ai eu un tas de classes Static Business Layer qui ont implémenté des méthodes CRUD comme "Créer", "Lire", "Mettre à jour", "Supprimer" pour chaque type d'entité comme "Utilisateur", "Équipe", ect .. Ensuite, j'ai créé une base contrôle qui avait une propriété abstraite pour la classe Business Layer 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

La plupart des gens semblent oublier que dans les classes OOP, il y a aussi des objets, et donc ils ont des messages, qui pour une raison quelconque c # appelle "méthode statique". Le fait qu'il existe des différences entre les objets d'instance et les objets de classe ne montre que des failles ou des lacunes dans le langage. Optimiste sur c # si ...


2
Le problème est que cette question ne concerne pas "in OOP". Il s'agit de "en C #". En C #, il n'y a pas de "messages".
John Saunders

1

OK, voici un exemple de besoin d'une «méthode de type». Je crée l'un d'un ensemble de classes basé sur du XML source. J'ai donc un

  static public bool IsHandled(XElement xml)

fonction qui est appelée à son tour sur chaque classe.

La fonction doit être statique car sinon nous perdons du temps à créer des objets inappropriés. Comme le souligne @Ian Boyde, cela pourrait être fait dans une classe d'usine, mais cela ajoute simplement de la complexité.

Ce serait bien de l'ajouter à l'interface pour forcer les implémenteurs de classe à l'implémenter. Cela n'entraînerait pas de surcharge importante - ce n'est qu'une vérification de l'heure de compilation / liaison et n'affecte pas la table virtuelle.

Cependant, ce serait également une amélioration assez mineure. Comme la méthode est statique, moi en tant qu'appelant, je dois l'appeler explicitement et donc obtenir une erreur de compilation immédiate si elle n'est pas implémentée. Le permettre d'être spécifié sur l'interface signifierait que cette erreur survient légèrement plus tôt dans le cycle de développement, mais cela est trivial par rapport à d'autres problèmes d'interface brisée.

Il s'agit donc d'une caractéristique potentielle mineure qui, dans l'ensemble, est probablement préférable de la laisser de côté.


1

Le fait qu'une classe statique soit implémentée en C # par Microsoft en créant une instance spéciale d'une classe avec les éléments statiques n'est qu'une bizarrerie sur la façon dont la fonctionnalité statique est obtenue. Ce n'est pas un point théorique.

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

Voici un exemple très clair où pouvoir utiliser des interfaces avec des classes statiques serait très utile:

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 vérification pour m'assurer que je n'ai rien oublié. C'est comme le mauvais vieux temps de programmation avant OOP.


C'est une question ancienne; de nos jours, nous nous efforçons d'éviter les questions basées sur l'opinion car il est difficile d'y répondre avec autorité. La seule bonne façon de répondre à cette question serait de décrire les raisons pour lesquelles MS aurait dû, ou aurait dû avoir, pour le faire comme il l'a fait.
Nathan Tuggy

1

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

Cela dit, je recommande que lorsque vous souhaitez utiliser une méthode statique dans une interface et que vous ne le pouvez pas, utilisez plutôt une annotation. Vous obtiendrez la fonctionnalité que vous recherchez.


0

Je pense que la réponse courte est "parce qu'elle n'a aucune utilité". Pour appeler une méthode d'interface, vous avez besoin d'une instance du type. À partir des méthodes d'instance, vous pouvez appeler toutes les méthodes statiques de votre choix.


0

Je pense que la question est de savoir que C # a besoin d'un autre mot-clé, précisément pour ce genre 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 ce type est inconnu. Mais une fois que le type sera connu, il deviendra statique. "Statique non résolu" est l'idée - ce n'est pas encore statique, mais une fois que nous connaîtrons le type de réception, il le sera. C'est un très bon concept, c'est pourquoi les programmeurs continuent de le demander. Mais cela ne correspondait pas tout à fait à la façon dont les concepteurs pensaient la langue.

Comme il n'est pas disponible, j'ai pris l'habitude d'utiliser des méthodes non statiques de la manière indiquée ci-dessous. Pas exactement idéal, mais je ne vois aucune approche plus logique, du moins pas pour moi.

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

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

0

Selon le concept orienté objet Interface implémentée par les classes et ayant un contrat pour accéder à ces fonctions (ou méthodes) implémentées à l'aide de l'objet.

Donc, si vous voulez accéder aux méthodes du contrat d'interface, vous devez créer un objet. Il faut toujours que ce ne soit pas autorisé dans le cas des méthodes statiques. Les classes, méthodes et variables statiques 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 nécessitent pas de création d'objet.


0

Conceptuellement il n'y a aucune raison pour qu'une interface ne puisse 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 des interfaces. Si "classe SomeBaseClass" implémente "interface ISomeInterface" et "classe SomeDerivedClass: SomeBaseClass, ISomeInterface" implémente également l'interface, une méthode statique pour implémenter une méthode d'interface échouera à la compilation car une méthode statique ne peut pas avoir la même signature qu'une méthode d'instance (ce qui être présent dans la classe de base pour implémenter l'interface).

Une classe statique est fonctionnellement identique à un singleton et sert le même objectif qu'un singleton avec une syntaxe plus propre. Puisqu'un singleton peut implémenter une interface, les implémentations d'interfaces par statique sont conceptuellement valides.

Donc, cela se résume simplement à la limitation des conflits de noms C # par exemple et aux méthodes statiques du même nom à travers 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éthode statique (interfaces).


-1

Lorsqu'une classe implémente une interface, elle crée une instance pour les membres de l'interface. Alors qu'un type statique n'a pas d'instance, il n'y a aucun intérêt à avoir des signatures statiques dans une interface.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.