Jak mogę zaimplementować metody statyczne w interfejsie?

92

Mam bibliotekę DLL C ++ innej firmy, którą wywołuję z języka C #.

Metody są statyczne.

Chcę to wyekstrahować, aby przeprowadzić testy jednostkowe, więc utworzyłem interfejs z metodami statycznymi, ale teraz mój program zawiera błędy:

Modyfikator „static” nie jest prawidłowy dla tej pozycji

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

Jak mogę osiągnąć tę abstrakcję?

Mój kod wygląda tak

private IInterfaceWithStaticMethods MyInterface;

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

public void MyMethod()
{
  MyInterface.StaticMethod();
}
Jon
źródło
3
Może możesz to zrobić za pomocą metod rozszerzających: stackoverflow.com/questions/1243921/…
hcb

Odpowiedzi:

47

Nie można definiować statycznych elementów członkowskich w interfejsie w C #. Interfejs to umowa dotycząca instancji .

Zalecałbym utworzenie interfejsu tak, jak obecnie, ale bez słowa kluczowego static. Następnie utwórz klasę, StaticIInterfacektóra implementuje interfejs i wywołuje statyczne metody C ++. Aby wykonać testy jednostkowe, utwórz kolejną klasę FakeIInterface, która również implementuje interfejs, ale robi to, co jest potrzebne do obsługi testów jednostkowych.

Po zdefiniowaniu tych dwóch klas możesz utworzyć taką, której potrzebujesz dla swojego środowiska, i przekazać ją do MyClasskonstruktora.

davisoa
źródło
64
-1 do powiedzenia An interface is a contract, not an implementation.- to prawda, ale tutaj zupełnie nieistotne ( non sequitur ), ponieważ metoda statyczna nie jest częścią samej implementacji - implementacja z definicji opiera się na danych , które z kolei są niedostępne dla statycznych składowych. 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.- pamiętaj, że staticczłonkowie są zwykle metodami użytkowymi .
2
Rozumiem i zgadzam się z Twoimi wypowiedziami i czuję, że Twój komentarz jest również ważnym kontekstem. Mimo że. Projektując interfejs należy myśleć o nim jak o kontrakcie, co oznacza, że ​​metody statyczne nie mają zastosowania. Pomyślałem, że powinienem to zostawić, aby pomóc niektórym ludziom zrozumieć przeznaczenie interfejsu. Czy społeczność uważa, że ​​należy go usunąć?
davisoa
1
Częściowo się zgadzam, że An interface is a contract, not an implementationjest to bezużyteczne, czasami trochę kontekstualizacji naprawdę pomaga. I całkowicie się z tym zgadzam static method is not a part of implementation itself , metody statyczne mają implementację, stają się częścią implementacji tylko wtedy, gdy są używane jako implementacja w implementacji innej metody. Jednak mój słownik jest oparty na tym, czego się nauczyłem, o ile wiem, terminologia naprawdę różni się również w zależności od języka programowania. Metody statyczne nie mogą być interfejsami, ponieważ i tak może istnieć tylko 1 implementacja.
CoffeDeveloper
Wyobraź sobie, że mam IPersonumowę, która stanowi, że GetCountrypoda nazwę kraju pochodzenia osoby ... FrenchPersonwszystkie podmioty będą mówić „Francja” i GermanPersonwszystkie powiedzą „Niemcy”, co jest również przydatne, gdy różne typy podmiotów dzielą tę samą tabelę (danych), jak np. MS Powiedzmy Connection, Azure One Posti Commentsą przechowywane w UsersAzureTable, więc jednostki drzewa mają wspólne informacje, IUsersmogą mieć GetTableNamemetodę statyczną ...
Serge
@vaxquis - IMHO, „to umowa” byłoby istotne, gdyby zdanie zostało przeformułowane: „Interfejs jest umową dla instancji” . Statyczne elementy członkowskie są częścią typu; to przeredagowane zdanie mówi (poprawnie), że nie mają one znaczenia w umowie instancji. Myślę więc, że problem polega tylko na nieprecyzyjnym sformułowaniu, a nie na braku sekwencji.
ToolmakerSteve
112

Interfejsy nie mogą mieć statycznych elementów członkowskich, a statyczne metody nie mogą być używane jako implementacja metod interfejsu.

Co możesz zrobić, to użyć jawnej implementacji interfejsu:

public interface IMyInterface
{
    void MyMethod();
}

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

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

Alternatywnie można po prostu użyć metod niestatycznych, nawet jeśli nie mają one dostępu do poszczególnych elementów członkowskich instancji.

Danny Varod
źródło
18
Dla każdego, kto zastanawia się, dlaczego ktoś miałby chcieć to zrobić, jest to szczególnie przydatne podczas pisania testów jednostkowych / integracyjnych dla starszego kodu, który implementuje metody statyczne.
Dezzamondo
Ta technika działała bardzo dobrze do implementacji szybkiego interfejsu API RESTful, który wymagał utrwalania danych, ale nie mógł używać bazy danych. Implementacja działała tylko z obiektami C # w pamięci, więc nie było miejsca do przechowywania danych, ale użycie właściwości statycznej zmniejszyło potrzebę korzystania z bazy danych w pamięci przy użyciu EF Core lub SQLite.
gware
19

Statyczne elementy członkowskie są całkowicie legalne w środowisku CLR, ale nie w języku C #.

Możesz zaimplementować trochę kleju w IL, aby połączyć szczegóły implementacji.

Nie masz pewności, czy kompilator C # pozwoliłby na ich wywoływanie?

Patrz: 8.9.4 Definicja typu interfejsu ECMA-335.

Typy interfejsów są z konieczności niekompletne, ponieważ nie mówią nic o reprezentacji wartości typu interfejsu. Z tego powodu definicja typu interfejsu nie powinna zawierać definicji pól dla wartości typu interfejsu (tj. Pól instancji), chociaż może deklarować pola statyczne (patrz punkt 8.4.3).

Podobnie definicja typu interfejsu nie powinna zapewniać implementacji żadnych metod na wartościach tego typu. Jednak definicja typu interfejsu może - i zwykle to robi - definiuje kontrakty metod (nazwa metody i podpis metody), które będą implementowane przez obsługiwane typy. Definicja typu interfejsu może definiować i implementować metody statyczne (patrz punkt 8.4.3), ponieważ metody statyczne są powiązane z samym typem interfejsu, a nie z jakąkolwiek wartością typu.

leppie
źródło
10
Dla porównania, CLS Rule 19: CLS-compliant interfaces shall not define static methods, nor shall they define fields.dalej stwierdza się, że konsumenci zgodni z CLS mogą odrzucić tego rodzaju interfejsy. Około rok temu próbowałem wywołać metodę statyczną w interfejsie, a kompilator C # nie mógł jej skompilować.
Christopher Currens
W nawiązaniu do @ChristopherCurrens uwaga na temat 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. ma sens, że jeśli CLS dotyczy interoperacyjności między różnymi językami .NET, a C # nie zezwala na statyczne elementy członkowskie w interfejsie, to CLS również by ich zabronił, aby zapewnić biblioteki w inne języki .NET można wywoływać z poziomu C #.
Simon Tewsi,
17

Można zdefiniować metody statyczne w języku C # 8, ale należy zadeklarować dla nich domyślną treść.

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

lub jeśli nie chcesz mieć żadnej domyślnej treści, po prostu wyrzuć wyjątek:

    public interface IMyInterface
    {
          static string GetHello() =>  throw new NotImplementedException() ;
          static void WriteWorld() => throw new NotImplementedException();
    }
AliReza
źródło
Wygląda na to, że statyczne elementy składowe w interfejsach są dość bezużyteczne, ponieważ nie można uzyskać do nich dostępu za pomocą instancji interfejsu. Przynajmniej w C # 8.
Pavel Sapehin
3
z punktu widzenia implementacji interfejsu, masz prawo. to jest bezużyteczne. ale w ten sposób przynajmniej na pewno masz zaimplementowaną metodę w każdej klasie używającej tego interfejsu. (jest to rodzaj opcjonalnej implementacji interfejsów)
AliReza
5

Możesz go przywołać z refleksją:

MyInterface.GetType().InvokeMember("StaticMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);
John Koerner
źródło
5
A jeśli nie masz instancji MyInterface, możesz użyć „typeOf (MyInterface)” zamiast „myInterface.GetType ()”.
RenniePet
W tamtym czasie wydawało się to dobrym pomysłem i mogę kontynuować to przez refleksję, ale jedno małe ostrzeżenie: staje się bardziej problematyczne, jeśli program zostanie zaciemniony w taki sposób, że nazwa metody StaticMethod zostanie zmieniona.
RenniePet
1
@RenniePet: Możesz częściowo poradzić sobie ze zmianą nazwy StaticMethod, używając zamiast tego nameof (StaticMethod). MOŻE pomóc z obfuskatorem, w zależności od tego, jak zmienia nazwę. Jeśli zrobisz to w ten sposób, zobaczysz przynajmniej błąd czasu kompilacji.
Brent Rittenhouse
Refleksja jest zbyt ekstremalna w tym przypadku
Stepan Ivanenko
3

C # „Dziesięć” pozwoli statycznym członkom na interfejsach , wraz z rolami. Jest to ogromny krok naprzód, który pozwoli również na przeciążenie ogólnego operatora, bez żadnej refleksji. Oto przykładowy fragment kodu, jak to działa, przy użyciu klasycznego monoidu, który jest po prostu żargonem oznaczającym „coś, co można dodać”. Zaczerpnięte bezpośrednio z 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

Dodatkowe zasoby:

Jeremy Bytes: statyczne elementy członkowskie interfejsu C # 8

EDYTOWAĆ

W tym poście pierwotnie stwierdzono, że statyczne elementy składowe interfejsu zostaną dodane w C # 8.0 , co nie jest prawdą, źle zinterpretowałem słowa Madsa Torgersena w filmie. Oficjalny przewodnik po C # 8.0 nie mówi jeszcze o statycznych elementach interfejsu, ale jest jasne, że pracowali nad tym już od dłuższego czasu.

Tamas Hegedus
źródło
1

Dlaczego nie można mieć metody statycznej na interfejsie: Dlaczego C # nie zezwala metodom statycznym na zaimplementowanie interfejsu?

Sugerowałbym jednak usunięcie metod statycznych na rzecz metod instancji. Jeśli nie jest to możliwe, można opakować wywołania metod statycznych wewnątrz metody instancji, a następnie utworzyć interfejs do tego celu i na jego podstawie uruchomić testy jednostkowe.

to znaczy

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

public interface IStaticWrapper
{
    void MyMethod();
}

public class MyClass : IStaticWrapper
{
    public void MyMethod()
    {
        MyStaticClass.MyStaticMethod();
    }
}
Justin Pihony
źródło
jaka jest zaleta korzystania z interfejsu z klasą statyczną nad używaniem samego interfejsu?
Selen
1

C # 8 umożliwia statyczne składowe w interfejsach

Począwszy od C # 8,0, interfejs może definiować domyślną implementację dla członków. Może również definiować statyczne elementy składowe, aby zapewnić pojedynczą implementację dla wspólnej funkcjonalności.

interfejs (odwołanie w C #)

Na przykład

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

var something = IGetSomething.Something;
Christian Findlay
źródło