Bir arabirimde statik yöntemleri nasıl uygulayabilirim?


92

C # 'dan çağırdığım bir 3. parti C ++ DLL var.

Yöntemler statiktir.

Bazı birim testleri yapmak için onu soyutlamak istiyorum, bu yüzden içindeki statik yöntemlerle bir arayüz oluşturdum, ancak şimdi program hatalarım:

'Statik' değiştiricisi bu öğe için geçerli değil

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

Bu soyutlamayı nasıl başarabilirim?

Kodum buna benziyor

private IInterfaceWithStaticMethods MyInterface;

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

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

3
Belki uzantı yöntemleriyle yapabilirsiniz: stackoverflow.com/questions/1243921/…
hcb

Yanıtlar:


47

C # 'da bir arabirimde statik üyeler tanımlayamazsınız. Arayüz, örnekler için bir sözleşmedir .

Arayüzü şu anda olduğu gibi, ancak static anahtar sözcüğü olmadan oluşturmanızı tavsiye ederim. Ardından StaticIInterface, arabirimi uygulayan ve statik C ++ yöntemlerini çağıran bir sınıf oluşturun . Birim testi yapmak için FakeIInterface, arayüzü de uygulayan ancak birim testlerinizi işlemek için ihtiyacınız olanı yapan başka bir sınıf oluşturun .

Bu 2 sınıfı tanımladıktan sonra, ortamınız için ihtiyacınız olanı oluşturabilir ve bunu MyClassyapıcısına aktarabilirsiniz .


64
-1 demek için An interface is a contract, not an implementation.- bu doğrudur, ancak burada tamamen alakasızdır ( ardışık olmayan ), çünkü statik yöntem uygulamanın kendisinin bir parçası değildir - uygulama, tanım gereği verilere dayanır ve bu da statik üyeler için erişilemez . 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.- staticüyelerin genellikle faydalı yöntemler olduğunu unutmayın .

2
İfadelerinizi anlıyorum ve kabul ediyorum ve yorumunuzun da önemli bir bağlam olduğunu düşünüyorum. Olmasına rağmen. Bir arayüz tasarlarken, bunu bir sözleşme olarak düşünmek gerekir, bu da statik yöntemlerin geçerli olmadığı anlamına gelir. Bazı insanların bir arayüzün amacını anlamasına yardımcı olmak için orada bırakmam gerektiğini düşündüm. Topluluk kaldırılması gerektiğini düşünüyor mu?
davisoa

1
An interface is a contract, not an implementationYararsız olduğuna kısmen katılıyorum , bazen biraz bağlamsallaştırma gerçekten yardımcı oluyor. Ve ben tamamen katılıyorum static method is not a part of implementation itself statik yöntemler sahip başka yöntemin uygulanmasında uygulama olarak kullanılan tek onlar uygulamasının parçası haline, bir uygulama. Ancak benim sözlüğüm öğrenilenlere dayanıyor, bildiğim kadarıyla terminoloji de programlama diline göre değişiyor. Statik yöntemler arayüz olamaz çünkü zaten sadece 1 uygulama olabilir.
CoffeDeveloper

Kişinin menşe ülke adını vereceğini IPersonbelirten bir sözleşmem olduğunu hayal edin GetCountry... FrenchPersonkuruluşların tümü "Fransa" GermanPersondiyecek ve tümü "Almanya" diyecek, aynı zamanda farklı türdeki kuruluşlar aynı (Veri) Tabloyu MS gibi paylaştığında da yararlıdır. Azure bir, demek Connection, Postve Commentsaklanır Usersağaç kişiler paylaşılan bilgi var bu yüzden, AzureTable IUsersolabilir GetTableNamestatik yöntemi ...
Serge

@vaxquis - IMHO, "onun bir sözleşmesi" cümle yeniden ifade edilmiş olsaydı alakalı olurdu: "Arayüz, örnekler için bir sözleşmedir . Statik üyeler türün bir parçasıdır; bu yeniden ifade edilmiş cümle, (doğru) bir örnek sözleşmesinde hiçbir anlamlarının olmadığını söylüyor. Bu yüzden sorunun sadece belirsiz bir ifade olduğunu düşünüyorum, bir ardıllık değil.
ToolmakerSteve

112

Arabirimlerin statik üyeleri olamaz ve statik yöntemler arabirim yöntemlerinin uygulanması olarak kullanılamaz.

Yapabilecekleriniz, açık bir arayüz uygulaması kullanmaktır:

public interface IMyInterface
{
    void MyMethod();
}

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

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

Alternatif olarak, herhangi bir örneğe özel üyeye erişmeseler bile statik olmayan yöntemleri kullanabilirsiniz.


18
Birinin neden bunu yapmak isteyeceğini merak eden herkes için, özellikle statik yöntemleri uygulayan eski kod için birim / entegrasyon testleri yazarken faydalıdır.
Dezzamondo

Bu teknik, verileri kalıcı hale getirmek için gereken ancak bir veritabanı kullanamayan hızlı bir RESTful API uygulamak için çok iyi çalıştı. Uygulama yalnızca bellek içi C # nesneleriyle çalışıyordu, bu nedenle verileri depolamak için yer yoktu, ancak statik bir özellik kullanmak EF Core veya SQLite kullanan bir bellek içi veritabanı ihtiyacını hafifletti.
gware

19

Statik üyeler CLR'de tamamen yasaldır, sadece C # değil.

Uygulama ayrıntılarını bağlamak için IL'de biraz yapıştırıcı uygulayabilirsiniz.

C # derleyicisinin onları aramaya izin verip vermeyeceğinden emin değil misiniz?

Bakınız: 8.9.4 Arayüz tipi tanımı ECMA-335.

Arabirim türleri, arabirim türünün değerlerinin gösterimi hakkında hiçbir şey söylemediklerinden, zorunlu olarak eksiktir. Bu nedenle, bir arabirim türü tanımı, statik alanları bildirebilmesine rağmen arabirim türünün değerleri (yani örnek alanları) için alan tanımları sağlamamalıdır (bkz. §8.4.3).

Benzer şekilde, bir arayüz türü tanımı, türünün değerleri üzerinde herhangi bir yöntem için uygulamalar sağlamayacaktır. Ancak, bir arayüz tipi tanımı, destekleyici tipler tarafından uygulanması gereken yöntem sözleşmelerini (yöntem adı ve yöntem imzası) tanımlayabilir ve genellikle yapar. Bir arabirim türü tanımı, statik yöntemler tanımlayabilir ve uygulayabilir (bkz. §8.4.3), çünkü statik yöntemler, türün herhangi bir değeri yerine arabirim türünün kendisiyle ilişkilendirilir.


10
Referans olarak, CLS Rule 19: CLS-compliant interfaces shall not define static methods, nor shall they define fields.CLS uyumlu tüketicilerin bu tür arayüzleri reddetmesinin uygun olduğunu söylemeye devam ediyor. Yaklaşık bir yıl önce bir arabirimde statik bir yöntem çağırmayı denedim ve C # derleyicisi bunu derlemedi.
Christopher Currens

@ChristopherCurrens'in CLS hakkındaki notuna ek olarak: 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. CLS, farklı .NET dilleri arasında birlikte çalışabilirlik hakkındaysa ve C # bir arabirimde statik üyelere izin vermiyorsa, CLS'nin bunları da yasaklayacağı anlamına gelir. diğer .NET dilleri C # 'dan çağrılabilir.
Simon Tewsi

17

Statik yöntemleri c # 8'de tanımlayabilirsiniz, ancak bunun için varsayılan bir gövde belirtmelisiniz.

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

veya herhangi bir varsayılan gövdeye sahip olmak istemiyorsanız, sadece bir istisna atabilirsiniz:

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

Görünüşe göre arayüzlerdeki statik üyeler oldukça işe yaramaz çünkü onlara arayüz örneğiyle erişemezsiniz. En azından C # 8'de.
Pavel Sapehin

3
Arayüz uygulama bakış açısı olarak, hakkınız. işe yaramaz. ancak bu şekilde en azından bu arayüzü kullanan her sınıfta uygulanan bir yönteme sahip olduğunuzdan emin olabilirsiniz. (bu, arabirimler için isteğe bağlı bir uygulamadır)
AliReza

5

Bunu derinlemesine düşünerek çağırabilirsiniz:

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

5
Ve bir MyInterface örneğiniz yoksa, "myInterface.GetType ()" yerine "typeOf (MyInterface)" kullanabilirsiniz.
RenniePet

O zamanlar iyi bir fikir gibi görünüyordu ve bunu derinlemesine düşünerek yapmaya devam edebilirim, ancak küçük bir uyarı: StaticMethod yöntemi yeniden adlandırılacak şekilde program karmaşık hale gelirse daha sorunlu hale gelir.
RenniePet

1
@RenniePet: Bunun yerine nameof (StaticMethod) kullanarak StaticMethod yeniden adlandırma ile kısmen ilgilenebilirsiniz. Nasıl yeniden adlandırıldığına bağlı olarak bir obfuscator ile yardımcı olabilir. Bu şekilde yaparsanız, en azından bir derleme zamanı hatası görürsünüz.
Brent Rittenhouse

Düşünce bu dava için çok aşırı
Stepan Ivanenko

3

C # "Ten", rollerin yanı sıra arayüzlerde statik üyelere izin verecek . Bu ileriye doğru büyük bir adımdır, herhangi bir yansıma kullanmadan genel operatör aşırı yüklemesine de izin verecektir. Burada, sadece "eklenebilecek bir şey" demek için kullanılan bir jargon olan klasik monoid örneği kullanan bir örnek pasaj bulunmaktadır. Doğrudan Mads Torgersen'den alınmıştır: C # Geleceğe :

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

Ek kaynaklar:

Jeremy Bytes: C # 8 arabirim statik üyeleri

DÜZENLE

Bu gönderi başlangıçta belirtilen arayüz statik üyeleri C # 8.0'a eklenecek , bu doğru değil, Mads Torgersen'in videodaki sözlerini yanlış yorumladım. Resmi C # 8.0 kılavuzu henüz statik arayüz üyelerinden bahsetmiyor, ancak oldukça uzun süredir üzerinde çalıştıkları açık.


1

Neden bir arabirimde statik bir yönteme sahip olamayacağınıza gelince: C # Statik Yöntemlerin Arabirimi Uygulamasına Neden İzin Vermiyor?

Ancak, örnek yöntemler lehine statik yöntemlerin kaldırılmasını öneririm. Bu mümkün değilse, statik yöntem çağrılarını bir örnek yöntemin içine sarabilir ve ardından bunun için bir arabirim oluşturabilir ve bundan birim testlerinizi çalıştırabilirsiniz.

yani

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

public interface IStaticWrapper
{
    void MyMethod();
}

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

Statik sınıfla arabirim kullanmanın yalnızca arabirimi kullanmaya göre avantajı nedir?
Selen

1

C # 8 Arayüzlerde Statik Üyelere İzin Verir

C # 8.0 ile başlayarak, bir arayüz, üyeler için varsayılan bir uygulama tanımlayabilir. Ortak işlevsellik için tek bir uygulama sağlamak için statik üyeleri de tanımlayabilir.

arayüz (C # Referansı)

Örneğin

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

var something = IGetSomething.Something;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.