¿Cómo puedo implementar métodos estáticos en una interfaz?

92

Tengo una DLL de C ++ de terceros a la que llamo desde C #.

Los métodos son estáticos.

Quiero abstraerlo para hacer algunas pruebas unitarias, así que creé una interfaz con los métodos estáticos, pero ahora mi programa tiene errores con:

El modificador 'estático' no es válido para este artículo

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

¿Cómo puedo lograr esta abstracción?

Mi código se ve así

private IInterfaceWithStaticMethods MyInterface;

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

public void MyMethod()
{
  MyInterface.StaticMethod();
}
Jon
fuente
3
Tal vez pueda hacerlo con métodos de extensión: stackoverflow.com/questions/1243921/…
hcb

Respuestas:

47

No puede definir miembros estáticos en una interfaz en C #. Una interfaz es un contrato por instancias .

Recomendaría crear la interfaz como está actualmente, pero sin la palabra clave estática. Luego crea una clase StaticIInterfaceque implemente la interfaz y llame a los métodos estáticos de C ++. Para hacer pruebas unitarias, cree otra clase FakeIInterface, que también implemente la interfaz, pero haga lo que necesita para manejar sus pruebas unitarias.

Una vez que haya definido estas 2 clases, puede crear la que necesita para su entorno y pasarla al MyClassconstructor de.

davisoa
fuente
64
-1 por decir An interface is a contract, not an implementation.- eso es cierto, pero completamente irrelevante ( non sequitur ) aquí, ya que el método estático no es parte de la implementación en sí ; la implementación, por definición, se basa en datos , que, a su vez, son inaccesibles para los miembros estáticos. 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.- Tenga en cuenta que los staticmiembros suelen ser métodos de utilidad .
2
Entiendo y estoy de acuerdo con sus declaraciones, y creo que su comentario también es un contexto importante. A pesar de que. al diseñar una interfaz, se debe pensar en ella como un contrato, lo que implica que no se aplican métodos estáticos. Pensé que debería dejarlo allí para ayudar a algunas personas a comprender el propósito de una interfaz. ¿Siente la comunidad que debería eliminarse?
davisoa
1
Estoy parcialmente de acuerdo en que An interface is a contract, not an implementationes inútil, a veces un poco de contextualización ayuda mucho. Y estoy totalmente de acuerdo con que static method is not a part of implementation itself los métodos estáticos tienen una implementación, se vuelven parte de la implementación solo si se usan como implementación en la implementación de otro método. Sin embargo, mi diccionario se basa en lo que aprendí, hasta donde yo sé, la terminología realmente varía también dependiendo del lenguaje de programación. Los métodos estáticos no pueden ser interfaces porque, de todos modos, solo puede haber una implementación.
CoffeDeveloper
Imagine que tengo un IPersoncontrato que establece que GetCountryle dará el nombre del país de origen de la persona ... FrenchPersontodas las entidades dirán "Francia" y GermanPersontodas dirán "Alemania", también útil cuando diferentes tipos de entidades comparten la misma tabla (de datos), como MS Azure uno, digamos Connection, Posty Commentse almacenan en UsersAzureTable, por lo que las entidades de árbol tienen una información compartida, IUserspodrían tener un GetTableNamemétodo estático ...
Serge
@vaxquis - En mi humilde opinión, "es un contrato" sería relevante si la oración fuera redactada nuevamente: `Una interfaz es un contrato para instancias . Los miembros estáticos son parte del tipo; esta oración reformulada dice (correctamente) que no tienen significado en un contrato de instancia. Por tanto, creo que el problema es simplemente una redacción imprecisa, no una falta de secuencia.
ToolmakerSteve
112

Las interfaces no pueden tener miembros estáticos y los métodos estáticos no se pueden utilizar como implementación de métodos de interfaz.

Lo que puede hacer es utilizar una implementación de interfaz explícita:

public interface IMyInterface
{
    void MyMethod();
}

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

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

Alternativamente, puede simplemente usar métodos no estáticos, incluso si no acceden a ningún miembro específico de la instancia.

Danny Varod
fuente
18
Para cualquiera que se pregunte por qué alguien querría hacer esto, es particularmente útil al escribir pruebas de unidad / integración para código heredado que implementa métodos estáticos.
Dezzamondo
Esta técnica funcionó muy bien para implementar una API RESTful rápida que necesitaba conservar los datos pero no podía usar una base de datos. La implementación solo funcionaba con objetos C # en memoria, por lo que no había lugar para almacenar datos, pero el uso de una propiedad estática alivió la necesidad de una base de datos en memoria con EF Core o SQLite.
gware
19

Los miembros estáticos son perfectamente legales en CLR, pero no C #.

Podría implementar algo de pegamento en IL para vincular los detalles de implementación.

Sin embargo, ¿no está seguro de si el compilador de C # permitiría llamarlos?

Consulte: 8.9.4 Definición del tipo de interfaz ECMA-335.

Los tipos de interfaz son necesariamente incompletos, ya que no dicen nada sobre la representación de los valores del tipo de interfaz. Por esta razón, una definición de tipo de interfaz no proporcionará definiciones de campo para valores del tipo de interfaz (es decir, campos de instancia), aunque puede declarar campos estáticos (véase §8.4.3).

De manera similar, una definición de tipo de interfaz no proporcionará implementaciones para ningún método sobre los valores de su tipo. Sin embargo, una definición de tipo de interfaz puede, y generalmente lo hace, definir contratos de método (nombre del método y firma del método) que se implementarán mediante tipos de soporte. Una definición de tipo de interfaz puede definir e implementar métodos estáticos (ver §8.4.3) ya que los métodos estáticos están asociados con el tipo de interfaz en sí en lugar de con cualquier valor del tipo.

leppie
fuente
10
Como referencia, CLS Rule 19: CLS-compliant interfaces shall not define static methods, nor shall they define fields.continúa diciendo que está bien que los consumidores que cumplen con CLS rechacen este tipo de interfaces. Intenté hace aproximadamente un año llamar a un método estático en una interfaz y el compilador de C # no lo compilaba.
Christopher Currens
Además de la nota de @ChristopherCurrens sobre 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. tiene sentido que si el CLS se trata de la interoperabilidad entre diferentes lenguajes .NET, y C # no permite miembros estáticos en una interfaz, CLS también los prohibiría, para garantizar que las bibliotecas estén en otros lenguajes .NET se pueden llamar desde C #.
Simon Tewsi
17

Puede definir métodos estáticos en c # 8, pero debe declarar un cuerpo predeterminado para él.

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

o si no quieres tener ningún cuerpo predeterminado, simplemente lanza una excepción:

    public interface IMyInterface
    {
          static string GetHello() =>  throw new NotImplementedException() ;
          static void WriteWorld() => throw new NotImplementedException();
    }
AliReza
fuente
Parece que los miembros estáticos en las interfaces son bastante inútiles porque no puede acceder a ellos por instancia de interfaz. Al menos en C # 8.
Pavel Sapehin
3
como punto de vista de la implementación de la interfaz, es su derecho. es inútil. pero de esta manera al menos está seguro de tener un método implementado en cada clase que esté usando esta interfaz. (esta es una especie de implementación opcional para interfaces)
AliReza
5

Podrías invocarlo con reflexión:

MyInterface.GetType().InvokeMember("StaticMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
John Koerner
fuente
5
Y si no tiene una instancia de MyInterface, puede usar "typeOf (MyInterface)" en lugar de "myInterface.GetType ()".
RenniePet
Parecía una buena idea en ese momento, y puedo continuar haciéndolo a través de la reflexión, pero una pequeña advertencia: se vuelve más problemático si el programa se ofusca de manera que el método StaticMethod cambia de nombre.
RenniePet
1
@RenniePet: podría lidiar parcialmente con el cambio de nombre de StaticMethod usando nameof (StaticMethod) en su lugar. PODRÍA ayudar con un ofuscador dependiendo de cómo cambie el nombre. Sin embargo, si lo hace de esta manera, al menos verá un error de tiempo de compilación.
Brent Rittenhouse
La reflexión es demasiado extrema para este caso
Stepan Ivanenko
3

C # "Ten" permitirá miembros estáticos en interfaces , junto con roles. Es un gran paso adelante, también permitirá la sobrecarga de operadores genéricos, sin ningún uso de reflexión. Aquí hay un fragmento de ejemplo de cómo funciona, utilizando el ejemplo clásico de monoide, que es solo jerga para decir "algo que se puede agregar". Tomado directamente de Mads Torgersen: C # en el futuro :

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

Recursos adicionales:

Jeremy Bytes: miembros estáticos de la interfaz C # 8

EDITAR

Esta publicación declaró originalmente que los miembros estáticos de la interfaz se agregarán en C # 8.0 , lo cual no es cierto, malinterpreté las palabras de Mads Torgersen en el video. La guía oficial de C # 8.0 aún no habla de los miembros de la interfaz estática, pero está claro que han estado trabajando en ella durante bastante tiempo.

Tamas Hegedus
fuente
1

En cuanto a por qué no puede tener un método estático en una interfaz: ¿Por qué C # no permite que los métodos estáticos implementen una interfaz?

Sin embargo, sugeriría eliminar los métodos estáticos en favor de los métodos de instancia. Si eso no es posible, entonces puede envolver las llamadas al método estático dentro de un método de instancia, y luego puede crear una interfaz para eso y ejecutar sus pruebas unitarias a partir de eso.

es decir

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

public interface IStaticWrapper
{
    void MyMethod();
}

public class MyClass : IStaticWrapper
{
    public void MyMethod()
    {
        MyStaticClass.MyStaticMethod();
    }
}
Justin Pihony
fuente
¿Cuál es la ventaja de usar una interfaz con clase estática sobre usar solo una interfaz?
Selen
1

C # 8 permite miembros estáticos en interfaces

A partir de C # 8.0, una interfaz puede definir una implementación predeterminada para los miembros. También puede definir miembros estáticos para proporcionar una implementación única para una funcionalidad común.

interfaz (referencia de C #)

P.ej

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

var something = IGetSomething.Something;
Christian Findlay
fuente