Cum pot implementa metode statice pe o interfață? (Programare, C#,.Net, Interfață)

Jon a intrebat.

Am un DLL C++ de la o terță parte pe care îl apelez din C#.

Metodele sunt statice.

Vreau să le abstractizez pentru a face niște teste unitare, așa că am creat o interfață cu metodele statice în ea, dar acum programul meu dă eroare cu:

Modificatorul „static” nu este valabil pentru acest element.

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

Cum pot realiza această abstractizare?

Codul meu arată în felul următor

private IInterfaceWithStaticMethods MyInterface;

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

public void MyMethod()
{
  MyInterface.StaticMethod();
}

Comentarii

  • Poate că puteți face acest lucru cu metode de extensie: stackoverflow.com/questions/1243921/… –  > Por hcb.
9 răspunsuri
davisoa

Nu poți defini membri statici pe o interfață în C#. O interfață este un contract pentru instanțe.

V-aș recomanda să creați interfața așa cum o faceți în prezent, dar fără cuvântul cheie static. Apoi creați o clasă StaticIInterface care implementează interfața și apelează metodele statice C++. Pentru a face teste unitare, creați o altă clasă FakeIInterface, , care implementează de asemenea interfața, dar care face ceea ce aveți nevoie pentru a gestiona testele unitare.

Odată ce ați definit aceste 2 clase, puteți crea cea de care aveți nevoie pentru mediul dumneavoastră și o puteți trece la MyClass.

Comentarii

    69

  • -1 pentru a spune An interface is a contract, not an implementation. – că este adevărat, dar complet irelevant (non sequitur) aici, deoarece metoda statică nu este o parte a implementării în sine – implementarea, prin definiție, se bazează pe date, , care, la rândul lor, sunt, inaccesibile pentru membrii statici. 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. – Rețineți că static membrii sunt de obicei metode utilitare. – utilizator719662
  • Înțeleg și sunt de acord cu afirmațiile dvs. și consider că și comentariul dvs. este un context important. Deși. atunci când se proiectează o interfață, ar trebui să se gândească la ea ca la un contract, ceea ce implică faptul că metodele statice nu se aplică. M-am gândit că ar trebui să o las acolo pentru a ajuta unele persoane să înțeleagă scopul unei interfețe. Consideră comunitatea că ar trebui să fie eliminat? –  > Por davisoa.
  • Sunt parțial de acord că An interface is a contract, not an implementation este inutilă, uneori un pic de contextualizare ajută foarte mult. Și sunt total de acord cu static method is not a part of implementation itself , , metode statice au o implementare, ele devin parte a implementării doar dacă sunt folosite ca implementare în implementarea unei alte metode. Oricum dicționarul meu se bazează pe ceea ce am învățat, din câte știu eu, terminologia chiar variază și în funcție de limbajul de programare. Metodele statice nu pot fi interfețe, deoarece poate exista oricum doar o singură implementare. –  > Por CoffeDeveloper.
  • Imaginați-vă că am o IPerson contract care prevede că GetCountry va da numele țării de origine a persoanei… FrenchPerson entitățile vor spune toate „Franța” și GermanPerson vor spune toate „Germania”, de asemenea, util atunci când diferite tipuri de entități împart același tabel (de date), cum ar fi cel din MS Azure, să zicem Connection, , Post și Comment sunt stocate în Users AzureTable, astfel încât entitățile arborelui au o informație comună, IUsers ar putea avea GetTableName metoda statică… –  > Por Serge.
  • @vaxquis – IMHO, „este un contract” ar fi relevant dacă propoziția ar fi reformulată: „O interfață este un contract”. pentru instanțe. Membrii statici fac parte din tip; această propoziție reformulată spune (corect) că ei nu au nicio semnificație într-un contract de instanță. Prin urmare, cred că problema este doar o formulare imprecisă, nu un non sequitur. –  > Por ToolmakerSteve.
Danny Varod

Interfețele nu pot avea membri statici și metodele statice nu pot fi folosite ca implementare a metodelor de interfață.

Ceea ce puteți face este să folosiți o implementare explicită a interfeței:

public interface IMyInterface
{
    void MyMethod();
}

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

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

Alternativ, puteți utiliza pur și simplu metode non-statice, chiar dacă acestea nu accesează niciun membru specific instanței.

Comentarii

    20

  • Pentru cine se întreabă de ce ar dori cineva să facă acest lucru, este deosebit de util atunci când scrieți teste de unitate/integrare pentru codul moștenit care implementează metode statice. –  > Por Dezzamondo.
  • Această tehnică a funcționat foarte bine pentru implementarea unui API RESTful rapid care avea nevoie să persiste datele, dar nu putea folosi o bază de date. Implementarea funcționa doar cu obiecte C# în memorie, astfel încât nu exista un loc de stocare a datelor, dar utilizarea unei proprietăți statice a atenuat nevoia unei baze de date în memorie care să utilizeze EF Core sau SQLite. –  > Por gware.
  • @gware nu este modul în care aș recomanda rezolvarea acestei probleme – încercați să injectați structura de date în memorie în constructorul clasei non-statice. De asemenea, puteți face ca o proprietate obișnuită să acceseze un câmp static (de asemenea, nu este recomandat). –  > Por Danny Varod.
AliReza

Tu puteți definiți metode statice în c# 8, dar trebuie să declarați un corp implicit pentru acestea.

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

sau dacă nu doriți să aveți nici un corp implicit pur și simplu aruncați o excepție:

    public interface IMyInterface
    {
          static string GetHello() =>  throw new NotImplementedException() ;
          static void WriteWorld() => throw new NotImplementedException();
    }

Comentarii

  • Se pare că membrii statici din interfețe sunt destul de inutili pentru că nu îi poți accesa prin instanța interfeței. Cel puțin în C# 8. –  > Por Pavel Sapehin.
  • din punct de vedere al implementării interfeței, aveți dreptate. este inutil. dar în acest fel cel puțin sunteți sigur că veți avea o metodă implementată pe fiecare clasă care folosește această interfață. (acesta este un fel de implementare opțională pentru interfețe) – -.  > Por AliReza.
leppie

Membrii statici sunt perfect legali în CLR, doar că nu și în C#.

Ați putea implementa o legătură în IL pentru a lega detaliile de implementare.

Nu sunt sigur că compilatorul C# ar permite totuși apelarea lor?

A se vedea: 8.9.4 Definirea tipului de interfață ECMA-335.

Tipurile de interfață sunt în mod necesar incomplete, deoarece nu spun nimic despre reprezentarea valorilor tipului de interfață. Din acest motiv, o definiție a tipului de interfață nu trebuie să furnizeze definiții de câmp pentru valorile tipului de interfață (adică câmpuri de instanță), deși poate declara câmpuri statice (a se vedea §8.4.3).

În mod similar, o definiție a tipului de interfață nu trebuie să furnizeze implementări pentru nicio metodă pentru valorile tipului său. Cu toate acestea, o definiție a tipului de interfață poate – și de obicei o face – să definească contracte de metode (numele și semnătura metodei) care trebuie implementate de tipurile de suport. O definiție a tipului de interfață poate defini și implementa metode statice (a se vedea punctul 8.4.3), deoarece metodele statice sunt asociate cu tipul de interfață în sine și nu cu orice valoare a tipului.

Comentarii

  • Pentru referință, CLS Rule 19: CLS-compliant interfaces shall not define static methods, nor shall they define fields. Se precizează în continuare că este în regulă ca consumatorii care respectă CLS să respingă aceste tipuri de interfețe. Am încercat în urmă cu aproximativ un an să apelez o metodă statică pe o interfață și compilatorul C# nu a compilat-o. –  > Por Christopher Currens.
  • Ca urmare a notei lui @ChristopherCurrens despre 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. Este logic că, dacă CLS se referă la interoperabilitatea între diferite limbaje .NET, iar C# nu permite membri statici pe o interfață, atunci CLS ar trebui să îi interzică și pe aceștia, pentru a se asigura că bibliotecile din alte limbaje .NET pot fi apelate din C#. –  > Por Simon Tewsi.
John Koerner

Ați putea să o invocați cu ajutorul reflexiei:

MyInterface.GetType().InvokeMember("StaticMethod", BindingFlags.Static | BindingFlags.Public | BindingFlags.InvokeMethod, null, null, null);

Comentarii

  • Iar dacă nu aveți o instanță a interfeței mele, puteți folosi „typeOf(MyInterface)” în loc de „myInterface.GetType()”. –  > Por RenniePet.
  • Mi s-a părut o idee bună la momentul respectiv și s-ar putea să continui să fac acest lucru prin intermediul reflecției, dar un mic avertisment: devine mai problematic dacă programul este ofuscat astfel încât metoda StaticMethod să fie redenumită. –  > Por RenniePet.
  • @RenniePet: Ați putea rezolva parțial problema redenumirii StaticMethod folosind în schimb nameof(StaticMethod). S-ar putea să vă ajute cu un obfuscator în funcție de modul în care se redenumește. Dacă o faceți în acest fel, ați vedea cel puțin o eroare de compilare, totuși. –  > Por Brent Rittenhouse.
  • Reflectarea este prea extremă pentru acest caz –  > Por Stepan Ivanenko.
Tamas Hegedus

C# „Ten” va permite membri statici pe interfețe, , alături de roluri. Este un pas uriaș înainte, va permite și supraîncărcarea generică a operatorilor, fără a utiliza reflecția. Iată un fragment de exemplu despre cum funcționează, folosind exemplul clasic al monoidului, care este doar un jargon pentru a spune „ceva care poate fi adăugat”. Preluat direct din Mads Torgersen: C# în viitor:

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

Resurse suplimentare:

Jeremy Bytes: Membrii statici ai interfeței C# 8

EDITARE

Această postare a afirmat inițial că membrii statici ai interfeței vor fi adăugați în C# 8.0, , ceea ce nu este adevărat, am interpretat greșit cuvintele lui Mads Torgersen din videoclip. Ghidul oficial C# 8.0 nu vorbește încă despre membrii statici de interfață, dar este clar că se lucrează la acest lucru de ceva vreme.

Christian Findlay

C# 8 permite membrii statici pe interfețe

Începând cu C# 8.0, o interfață poate defini o implementare implicită pentru membri. De asemenea, poate defini membri statici pentru a oferi o singură implementare pentru funcționalități comune.

interfață (referință C#)

De ex.

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

var something = IGetSomething.Something;

Justin Pihony

În ceea ce privește motivul pentru care nu puteți avea o metodă statică pe o interfață: De ce nu permite C# ca metodele statice să implementeze o interfață?

Totuși, aș sugera eliminarea metodelor statice în favoarea metodelor de instanță. Dacă acest lucru nu este posibil, atunci ați putea înfășura apelurile de metode statice în interiorul unei metode de instanță, iar apoi puteți crea o interfață pentru aceasta și puteți rula testele unitare din aceasta.

adică

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

public interface IStaticWrapper
{
    void MyMethod();
}

public class MyClass : IStaticWrapper
{
    public void MyMethod()
    {
        MyStaticClass.MyStaticMethod();
    }
}

Comentarii

  • care este avantajul utilizării interfeței cu clasa statică în comparație cu utilizarea doar a interfeței? –  > Por Selen.
Walter Vehoeven

Nu văd alte probleme în afară de faptul că compilatorul crede că nu ar trebui să fac acest lucru. C# nu poate moșteni din mai mult de o clasă de bază, o adevărată pacoste atunci când ești obișnuit să poți face acest lucru, frontal poți face mai multe interfețe, așa că abuzez de asta pentru a introduce pe furiș funcțiile de care am nevoie 😉

Ar trebui să verificați dacă există null etc. Cu toate acestea, iată o versiune simplificată care implementează Parse pentru a obține o clasă dintr-un serviciu web sau o bază de date

/// <summary>
/// Implements parse
/// </summary>
/// <typeparam name="T">the type to parse</typeparam>
public interface IParse<T>
{ 
    /// <summary>
    /// implements parse from string to type
    /// </summary>
    /// <param name="text">value to parse</param>
    /// <returns></returns>
    static T Parse(string text)=>JsonConvert.DeserializeObject<T>(text, settings:new JsonSerializerSettings() { ConstructorHandling= ConstructorHandling.AllowNonPublicDefaultConstructor });

    /// <summary>
    /// implements parse from string to type
    /// </summary>
    /// <param name="text">value to parse</param>
    /// <param name="settings">the settings to us</param>
    /// <returns></returns>
    static T Parse(string text, JsonSerializerSettings settings) =>JsonConvert.DeserializeObject<T>(text, settings);
}

Apoi nu fac decât să apelez interfața în codul care are ca valoare de returnare List.

Iată un fragment în care citesc JSON din baza de date și îl alimentez cu un tip care are implementat Json

//some plugging code

using (var reader = cmd.ExecuteReader(behavior: CommandBehavior.CloseConnection | CommandBehavior.SingleResult))
{
    if (reader.HasRows)
    {
        while (reader.Read())
        {
            rows++;
            try
            {
                var json = reader.GetString(0);

                result.Add(IParse<T>.Parse(json));
            }
            catch
            {
                failed++;
            }
        }
    }
}
//other plugging code

Cu >ver versiunea 8 aveți implementarea implicită, astfel încât „cutia Pandorei este deschisă”