Comment puis-je implémenter des méthodes statiques sur une interface?

92

J'ai une DLL C ++ tierce que j'appelle à partir de C #.

Les méthodes sont statiques.

Je veux l'abstraire pour faire des tests unitaires, j'ai donc créé une interface avec les méthodes statiques, mais maintenant mes erreurs de programme avec:

Le modificateur «statique» n'est pas valide pour cet article

MyMethod cannot be accessed with an instance reference; qualify it with a type name instead

Comment puis-je réaliser cette abstraction?

Mon code ressemble à ceci

private IInterfaceWithStaticMethods MyInterface;

public MyClass(IInterfaceWithStaticMethods myInterface)
{
  this.MyInterface = myInterface;
}

public void MyMethod()
{
  MyInterface.StaticMethod();
}
Jon
la source
3
Peut-être que vous pouvez le faire avec des méthodes d'extension: stackoverflow.com/questions/1243921/…
hcb

Réponses:

47

Vous ne pouvez pas définir de membres statiques sur une interface en C #. Une interface est un contrat pour les instances .

Je recommanderais de créer l'interface telle que vous l'êtes actuellement, mais sans le mot-clé static. Créez ensuite une classe StaticIInterfacequi implémente l'interface et appelle les méthodes C ++ statiques. Pour faire des tests unitaires, créez une autre classe FakeIInterface, qui implémente également l'interface, mais fait ce dont vous avez besoin pour gérer vos tests unitaires.

Une fois ces 2 classes définies, vous pouvez créer celle dont vous avez besoin pour votre environnement et la transmettre au MyClassconstructeur de.

Davisoa
la source
64
-1 pour dire An interface is a contract, not an implementation.- c'est vrai, mais complètement hors de propos ( non sequitur ) ici, puisque la méthode statique ne fait pas partie de l'implémentation elle - même - l'implémentation, par définition, est basée sur des données , qui sont, à leur tour, inaccessibles pour les membres statiques. An interface type definition can define and implement static methods (see §8.4.3) since static methods are associated with the interface type itself rather than with any value of the type.- gardez à l'esprit que les staticmembres sont généralement des méthodes d'utilité .
2
Je comprends et suis d'accord avec vos déclarations, et j'estime que votre commentaire est également un contexte important. Bien que. lors de la conception d'une interface, il faut la considérer comme un contrat, ce qui implique que les méthodes statiques ne s'appliquent pas. J'ai pensé que je devrais le laisser là pour aider certaines personnes à comprendre le but d'une interface. La communauté pense-t-elle qu'il devrait être supprimé?
davisoa
1
Je suis partiellement d'accord que An interface is a contract, not an implementationc'est inutile, parfois un peu de contextualisation aide vraiment. Et je suis tout à fait d'accord avec static method is not a part of implementation itself , les méthodes statiques ont une implémentation, elles ne font partie de l'implémentation que si elles sont utilisées comme implémentation dans l'implémentation d'une autre méthode. Cependant, mon dictionnaire est basé sur ce que j'ai appris, pour autant que je sache, la terminologie varie vraiment aussi en fonction du langage de programmation. Les méthodes statiques ne peuvent pas être des interfaces car il ne peut y avoir qu'une seule implémentation.
CoffeDeveloper
Imaginez que j'ai un IPersoncontrat qui stipule que GetCountryje donne le nom du pays d'origine de la personne ... les FrenchPersonentités diront toutes «France» et GermanPersontoutes diront «Allemagne», également utile lorsque différents types d'entités partagent le même tableau (de données), comme MS Azure one, disons Connection, Postet Commentsont stockés dans UsersAzureTable, donc les entités d'arborescence ont une information partagée, IUserspourraient avoir GetTableNameune méthode statique ...
Serge
@vaxquis - IMHO, "c'est un contrat" ​​serait pertinent si la phrase était reformulée: `Une interface est un contrat pour les instances . Les membres statiques font partie du type; cette phrase reformulée dit (correctement) qu'ils n'ont aucun sens dans un contrat d'instance. Je pense donc que le problème est simplement une formulation imprécise, pas un non séquentiel.
ToolmakerSteve
112

Les interfaces ne peuvent pas avoir de membres statiques et les méthodes statiques ne peuvent pas être utilisées comme implémentation de méthodes d'interface.

Ce que vous pouvez faire, c'est utiliser une implémentation d'interface explicite:

public interface IMyInterface
{
    void MyMethod();
}

public class MyClass : IMyInterface
{
    static void MyMethod()
    {
    }

    void IMyInterface.MyMethod()
    {
        MyClass.MyMethod();
    }
}

Sinon, vous pouvez simplement utiliser des méthodes non statiques, même si elles n'accèdent à aucun membre spécifique à une instance.

Danny Varod
la source
18
Pour quiconque se demande pourquoi quelqu'un voudrait faire cela, c'est particulièrement utile lors de l'écriture de tests unitaires / d'intégration pour du code hérité qui implémente des méthodes statiques.
Dezzamondo
Cette technique fonctionnait très bien pour implémenter une API RESTful rapide qui devait conserver les données mais ne pouvait pas utiliser une base de données. L'implémentation ne fonctionnait qu'avec des objets C # en mémoire, il n'y avait donc pas de place pour stocker les données, mais l'utilisation d'une propriété statique réduisait le besoin d'une base de données en mémoire utilisant EF Core ou SQLite.
gware le
19

Les membres statiques sont parfaitement légaux dans le CLR, mais pas C #.

Vous pouvez implémenter un peu de glue dans IL pour relier les détails d'implémentation.

Vous ne savez pas si le compilateur C # permettrait de les appeler?

Voir: 8.9.4 Définition du type d'interface ECMA-335.

Les types d'interface sont forcément incomplets puisqu'ils ne disent rien sur la représentation des valeurs du type d'interface. Pour cette raison, une définition de type d'interface ne doit pas fournir de définitions de champ pour les valeurs du type d'interface (c'est-à-dire des champs d'instance), bien qu'elle puisse déclarer des champs statiques (voir le §8.4.3).

De même, une définition de type d'interface ne doit pas fournir d'implémentations pour aucune méthode sur les valeurs de son type. Cependant, une définition de type d'interface peut - et fait généralement - définir des contrats de méthode (nom de méthode et signature de méthode) qui doivent être implémentés par les types de prise en charge. Une définition de type d'interface peut définir et implémenter des méthodes statiques (voir §8.4.3) puisque les méthodes statiques sont associées au type d'interface lui-même plutôt qu'à n'importe quelle valeur du type.

leppie
la source
10
Pour référence, CLS Rule 19: CLS-compliant interfaces shall not define static methods, nor shall they define fields.il poursuit en disant qu'il est normal que les consommateurs conformes CLS rejettent ces types d'interfaces. J'ai essayé il y a environ un an d'appeler une méthode statique sur une interface et le compilateur C # ne la compilait pas.
Christopher Currens
Suite à la note de @ChristopherCurrens à propos du CLS: Common Language Specification (CLS) is a set of basic language features that .Net Languages needed.... When there is a situation to communicate Objects written in different .Net Complaint languages , those objects must expose the features that are common to all the languages. il est logique que si le CLS concerne l'interopérabilité entre différents langages .NET et que C # n'autorise pas les membres statiques sur une interface, alors le CLS les interdirait également, pour garantir les bibliothèques dans d'autres langages .NET peuvent être appelés à partir de C #.
Simon Tewsi
17

Vous pouvez définir des méthodes statiques en c # 8 mais vous devez déclarer un corps par défaut pour cela.

    public interface IMyInterface
    {
          static string GetHello() =>  "Default Hello from interface" ;
          static void WriteWorld() => Console.WriteLine("Writing World from interface");
    }

ou si vous ne voulez pas de corps par défaut, lancez simplement une exception:

    public interface IMyInterface
    {
          static string GetHello() =>  throw new NotImplementedException() ;
          static void WriteWorld() => throw new NotImplementedException();
    }
AliReza
la source
Il semble que les membres statiques dans les interfaces sont assez inutiles car vous ne pouvez pas y accéder par instance d'interface. Au moins en C # 8.
Pavel Sapehin
3
en tant que point de vue d'implémentation d'Interface, votre droit. c'est inutile. mais de cette façon au moins vous êtes sûr d'avoir une méthode implémentée sur chaque classe qui utilise cette interface. (c'est une sorte d'implémentation optionnelle pour les interfaces)
AliReza
5

Vous pouvez l'invoquer avec réflexion:

MyInterface.GetType().InvokeMember("StaticMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
John Koerner
la source
5
Et si vous ne disposez pas d'une instance de MyInterface, vous pouvez utiliser "typeOf (MyInterface)" au lieu de "myInterface.GetType ()".
RenniePet
Cela a semblé être une bonne idée à l'époque, et je peux continuer à le faire par réflexion, mais un petit avertissement: cela devient plus problématique si le programme est obscurci de sorte que la méthode StaticMethod soit renommée.
RenniePet
1
@RenniePet: Vous pouvez partiellement gérer le renommage StaticMethod en utilisant à la place nameof (StaticMethod). Cela PEUT aider avec un obfuscateur en fonction de la façon dont il renomme. Si vous le faites de cette façon, vous verrez au moins une erreur de compilation.
Brent Rittenhouse
La réflexion est trop extrême pour ce cas
Stepan Ivanenko
3

C # "Ten" va autoriser les membres statiques sur les interfaces , avec les rôles. C'est un énorme pas en avant, cela permettra également une surcharge des opérateurs génériques, sans aucune réflexion. Voici un extrait d'exemple de son fonctionnement, en utilisant l'exemple classique du monoïde, qui n'est qu'un jargon pour dire "quelque chose qui peut être ajouté". Tiré directement de Mads Torgersen: C # into the Future :

interface IMonoid<T>
{
    static T Zero { get; }
    static T operator +(T t1, T t2);
}

public static T AddAll<T>(T[] ts) where T : IMonoid<T>
{
    T result = T.Zero;
    foreach (T t in ts) { result += t; }
    return result;
}

role IntAddMonoid extends int : IMonoid<int>
{
    public static int Zero => 0;
}

IntAddMonoid[] values = new int[] {1, 2, 4, 8, 16, 32};
int sixtyThree = AddAll<IntAddMonoid>(values); // == 63

Ressources additionnelles:

Jeremy Bytes: membres statiques de l'interface C # 8

ÉDITER

Ce message a initialement déclaré que les membres statiques de l'interface seront ajoutés dans C # 8.0 , ce qui n'est pas vrai, j'ai mal interprété les mots de Mads Torgersen dans la vidéo. Le guide officiel C # 8.0 ne parle pas encore des membres de l'interface statique, mais il est clair qu'ils y travaillent depuis assez longtemps.

Tamas Hegedus
la source
1

Quant à savoir pourquoi vous ne pouvez pas avoir de méthode statique sur une interface: Pourquoi C # n'autorise-t-il pas les méthodes statiques à implémenter une interface?

Cependant, je suggérerais de supprimer les méthodes statiques au profit des méthodes d'instance. Si cela n'est pas possible, vous pouvez encapsuler les appels de méthode statique à l'intérieur d'une méthode d'instance, puis créer une interface pour cela et exécuter vos tests unitaires à partir de cela.

c'est à dire

public static class MyStaticClass
{
    public static void MyStaticMethod()
    {...}
}

public interface IStaticWrapper
{
    void MyMethod();
}

public class MyClass : IStaticWrapper
{
    public void MyMethod()
    {
        MyStaticClass.MyStaticMethod();
    }
}
Justin Pihony
la source
quel est l'avantage d'utiliser l'interface avec une classe statique par rapport à l'utilisation uniquement de l'interface?
Selen le
1

C # 8 autorise les membres statiques sur les interfaces

À partir de C # 8.0, une interface peut définir une implémentation par défaut pour les membres. Il peut également définir des membres statiques afin de fournir une seule implémentation pour une fonctionnalité commune.

interface (référence C #)

Par exemple

public interface IGetSomething
{
    public static string Something = "something";
}

var something = IGetSomething.Something;
Christian Findlay
la source