C # Neden Statik Yöntemlerin Arabirim Uygulamasına İzin Vermiyor?


447

C # neden bu şekilde tasarlandı?

Anladığım kadarıyla, bir arayüz sadece davranışı tanımlar ve belirli davranışın uygulandığı arayüzü uygulayan sınıflar için sözleşmeye bağlı bir yükümlülüğü tanımlamak amacına hizmet eder.

Sınıflar bu davranışı paylaşılan bir yöntemle uygulamak istiyorsa neden olmasın?

Aklımda ne var bir örnek:

// These items will be displayed in a list on the screen.
public interface IListItem {
  string ScreenName();
  ...
}

public class Animal: IListItem {
    // All animals will be called "Animal".
    public static string ScreenName() {
        return "Animal";
    }
....
}

public class Person: IListItem {

    private string name;

    // All persons will be called by their individual names.
    public string ScreenName() {
        return name;
    }

    ....

 }


1
Statik bir davranışı miras veya arayüz uygulamasıyla nasıl birleştirebileceğinizi görün: stackoverflow.com/a/13567309/880990
Olivier Jacot-Descombes

1
IListItem.ScreenName() => ScreenName()(C # 7 sözdizimi kullanılarak) statik yöntemi çağırarak açık bir şekilde arabirim yöntemini uygular. Buna miras eklediğinizde işler çirkinleşiyor (arayüzü yeniden uygulamak zorundasınız)
Jeroen Mostert

2
Herkesin beklemenin sona erdiğini bilmesini sağlayın! C # 8.0 statik arayüz yöntemlerine sahiptir: dotnetfiddle.net/Lrzy6y (OP'nin nasıl çalışmasını istediklerinden biraz farklı olsalar da - bunları uygulamak zorunda değilsiniz)
Jamie Twells

Yanıtlar:


221

Bunu neden yapamayacağınızı sorduğunuzda:

public interface IFoo {
    void Bar();
}

public class Foo: IFoo {
    public static void Bar() {}
}

Bu anlamsal olarak benim için bir anlam ifade etmiyor. Bir arabirimde belirtilen yöntemler, bir nesne ile etkileşim için sözleşmeyi belirtmek için orada olmalıdır. Statik yöntemler bir nesneyle etkileşime girmenize izin vermez - kendinizi uygulamanızın statik hale getirilebileceği konumda bulursanız, bu yöntemin gerçekten arayüzde olup olmadığını kendinize sormanız gerekebilir.


Örneğinizi uygulamak için, Animal'e statik bir bağlamdan erişilmesine izin veren bir const özelliği verir ve uygulamada bu değeri döndürürüm.

public class Animal: IListItem {
    /* Can be tough to come up with a different, yet meaningful name!
     * A different casing convention, like Java has, would help here.
     */
    public const string AnimalScreenName = "Animal";
    public string ScreenName(){ return AnimalScreenName; }
}

Daha karmaşık bir durum için, her zaman başka bir statik yöntem bildirebilir ve buna yetki verebilirsiniz. Bir örnek bulmaya çalışırken, hem statik hem de örnek bağlamda önemsiz olmayan bir şey yapmanın herhangi bir nedenini düşünemedim, bu yüzden size bir FooBar blob'u yedekleyeceğim ve bunun bir göstergesi olarak alacağım iyi bir fikir değil.


6
Mükemmel bir örnek! Ama mantığınızı anladığımdan emin değilim. Elbette derleyici statuc üyelerine de bakacak şekilde tasarlanmış olabilir mi? Örneklerin, bu yöntemlerin uygulanması için bir adres tablosu yok mu? Statik yöntemler bu tabloya eklenemedi mi?
Kramii

12
Bunun yararlı olabileceği bir durum var. Örneğin, tüm uygulayıcıların XElement argümanı alan bir GetInstance yöntemi uygulamasını istiyorum. Bunu arayüzde statik bir yöntem olarak tanımlayamıyorum veya arayüzden bir yapıcı imza talep etmiyorum.
oleks

25
Her iki tarafta da pek çok kişi ağırdı: "Bu hiç mantıklı değil" den "Bu bir hata, keşke yapabilseydin." (Bunun için geçerli kullanım durumları olduğunu düşünüyorum, bu yüzden burada sona erdi.) Bir çözüm olarak, örnek yöntemi yalnızca statik bir yönteme delege edebilir.
harpo

5
Parçayı temel arayüzde, public static object MethodName(this IBaseClass base)bir statik sınıf içinde olduğu gibi, bir genişletme yöntemi olarak da uygulayabilirsiniz . Bununla birlikte, olumsuz, bir arayüz kalıtımından farklı olarak - bu, bireysel mirasçıların metodolojiyi iyi geçersiz kılmalarına izin vermez / izin vermez.
Troy Alford

8
Jeneriklerle çok mantıklı olurdu. Örneğin, T: ISomeInterface {new T () olan bir şey <T> () geçersizdir. DoSomething1 (); T.DoSomething2 (); }
Francisco Ryan Tolmasky

173

Benim (basitleştirilmiş) teknik nedenim statik yöntemlerin vtable'da olmaması ve çağrı sitesinin derleme zamanında seçilmesidir. Geçersiz kılma veya sanal statik üyeleriniz olamaz. Daha fazla ayrıntı için, hiçbiri olmadığım bir CS grad veya derleyici kazanmasına ihtiyacınız olacak.

Siyasi sebepten dolayı, Eric Lippert'i (derleyici kazanır ve Waterloo Üniversitesi'nden Matematik, Bilgisayar bilimi ve Uygulamalı Matematik Lisansı (kaynak: LinkedIn ):

... statik yöntemlerin temel tasarım ilkesi, onlara isimlerini veren ilke ... Yani, yöntem yalnızca kodun statik analizi ile çözülebilir.

Lippert'in sözde tip yöntemine yer bıraktığını unutmayın:

Yani, bir türle ilişkili (statik gibi), boş olmayan bir "bu" bağımsız değişkeni almayan (bir örnek veya sanaldan farklı olarak), ancak çağrılan yöntemin oluşturulmuş T türüne ( derleme zamanında belirlenebilir olması gereken bir statikten farklı olarak).

ancak henüz yararlı olduğuna ikna olmamıştır.


5
Mükemmel, bu yazmak istediğim cevap - sadece uygulama detayını bilmiyordum.
Chris Marasti-Georg

5
Mükemmel cevap. Ve bu 'tip yöntemi' istiyorum! Pek çok durumda yararlı olabilir (bir tür / sınıf için meta verileri düşünün).
Philip Daubmeier

23
Bu doğru cevap. Birine bir arayüz iletirsiniz, bir yöntemi nasıl arayacaklarını bilmeleri gerekir. Bir arayüz sadece sanal bir yöntem tablosudur . Statik sınıfınızda buna sahip değil. Arayan bir yöntemi nasıl arayacağını bilemez . (Bu cevabı okumadan önce C # 'ın bilgiçlikçi olduğunu düşündüm. Şimdi bunun teknik bir sınırlama olduğunu, bir arayüz nedir olduğu ). Diğer insanlar bunun nasıl kötü bir tasarım olduğu hakkında konuşacaklar. Kötü bir tasarım değil - teknik bir sınırlama.
Ian Boyd

2
Soruyu gerçekten cevapladığı ve bilgiçlik ve sümüklü olmadığı için +1. Ian Boyd'un dediği gibi: "Bu kötü bir tasarım değil - teknik bir sınırlama."
Joshua Pech

3
İlişkili vtable ile statik bir sınıf için bir nesne oluşturmak tamamen mümkündür. Scala'nın objects'yi nasıl ele aldığına ve arayüzlerin nasıl uygulanmasına izin verildiğine bakın.
Sebastian Graf

97

Buradaki cevapların çoğu bütün noktayı kaçırıyor gibi görünüyor. Polimorfizm sadece örnekler arasında değil, aynı zamanda türler arasında da kullanılabilir. Jenerik kullandığımızda buna sıklıkla ihtiyaç duyulur.

Diyelim ki genel yöntemde type parametresi var ve onunla bazı işlemler yapmalıyız. Anlaşmak istemiyoruz çünkü kurucuların farkında değiliz.

Örneğin:

Repository GetRepository<T>()
{
  //need to call T.IsQueryable, but can't!!!
  //need to call T.RowCount
  //need to call T.DoSomeStaticMath(int param)
}

...
var r = GetRepository<Customer>()

Ne yazık ki, sadece "çirkin" alternatiflerle gelebilirim:

  • Yansıma çirkin kullanın ve arayüzler ve polimorfizm fikrini yener.

  • Tamamen ayrı fabrika sınıfı oluşturun

    Bu, kodun karmaşıklığını büyük ölçüde artırabilir. Örneğin, etki alanı nesnelerini modellemeye çalışıyorsak, her nesnenin başka bir depo sınıfına ihtiyacı olacaktır.

  • İstenen arabirim yöntemini başlatın ve çağırın

    Genel parametre olarak kullanılan sınıfların kaynağını kontrol etsek bile bunu uygulamak zor olabilir. Bunun nedeni, örneğin, örneklerin yalnızca iyi bilinen "DB'ye bağlı" durumda olması gerektiğidir.

Misal:

public class Customer 
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  void SomeOtherMethod() 
  { 
    //do work...
  }
}

statik arayüz problemini çözmek için anlık kullanımı kullanmak için aşağıdakileri yapmamız gerekir:

public class Customer: IDoSomeStaticMath
{
  //create new customer
  public Customer(Transaction t) { ... }

  //open existing customer
  public Customer(Transaction t, int id) { ... }

  //dummy instance
  public Customer() { IsDummy = true; }

  int DoSomeStaticMath(int a) { }

  void SomeOtherMethod() 
  { 
    if(!IsDummy) 
    {
      //do work...
    }
  }
}

Bu açıkça çirkin ve aynı zamanda gereksiz tüm diğer yöntemler için kodu karmaşık. Açıkçası, zarif bir çözüm de değil!


35
+1 için "Buradaki yanıtların çoğu bütün noktayı kaçırıyor gibi görünüyor"; hemen hemen tüm cevapların çoğunlukla

5
@Chris İşte bu kısıtlamaya tekrar vurmamı sağlayan özel bir örnek. Site yöneticisi tarafından sıfırlanabilen statik değişkenlerde belirli verileri önbelleğe aldıklarını belirtmek için sınıflara IResettable arabirimi eklemek istiyorum (örneğin, bir sipariş kategorisi listesi, KDV oranları kümesi, harici bir API'den alınan kategori listesi) DB ve harici API isabetlerini azaltmak için bu, statik bir yöntem sıfırlaması kullanır. Bu, hangi sınıfların sıfırlanabileceğini algılamayı otomatikleştirmemi sağlar. Bunu hala yapabilirim, ancak yöntem IDE'ye zorlanmaz veya otomatik olarak eklenmez ve umuduna dayanır.
mattmanser

6
@Crisris Katılıyorum, büyük bir atılma. Daha fazla mimari 'en iyi' çözüm olduğunda, her zaman bir dil kusurunun bir işaretidir. C # jenerik ve anonim yöntemlere sahip olduğu için hiç kimsenin bahsetmediği tüm kalıpları hatırlıyor musunuz?
mattmanser

3
"Where T: IQueryable, T: IDoSomeStaticMath" ya da benzerini kullanamaz mısınız?
Roger Willcocks

2
@ ChrisMarasti-Georg: Etrafında biraz kirli ama ilginç bir yol bu küçük yapı: public abstract class DBObject<T> where T : DBObject<T>, new()ve sonra tüm DB sınıflarını devralın DBObject<T>. Ardından, geri alma tuşunu soyut üst sınıfta T türü ile statik bir işlev haline getirebilir, bu işlevin yeni bir T nesnesi oluşturmasını sağlayabilir ve daha sonra String GetRetrieveByKeyQuery()elde etmek için o nesne üzerinde korumalı (üst sınıfta soyut olarak tanımlanır) çağırabilirsiniz. çalıştırılacak gerçek sorgu. Bu konu biraz kapalı olsa da
Nyerguds

20

Bunun eski bir soru olduğunu biliyorum, ama ilginç. Örnek iyi değil. Bir kullanım durumu gösterdiyseniz çok daha açık olacağını düşünüyorum:

string DoSomething <T> () burada T: ISomeFunction
{
  eğer (T.someFunction ())
    ...
}

Yalnızca bir arabirimi uygulamak için statik yöntemlere sahip olmak, istediğiniz şeyi elde edemezdi; bir arayüzün parçası olarak statik üyelerin olması gerekirdi . Bunun için birçok kullanım örneği hayal edebiliyorum, özellikle de bir şeyler yaratma söz konusu olduğunda. Yardımcı olabilecek iki yaklaşım önerebilirim:

  1. Type parametresi yukarıdaki DoSomething'e geçireceğiniz tür olacak statik bir genel sınıf oluşturun. Bu sınıfın her varyasyonu, bu türle ilgili şeyler tutan bir veya daha fazla statik üyeye sahip olacaktır. Bu bilgiler, her bir ilgi sınıfının bir "kayıt bilgileri" yordamı çağırmasını sağlayarak veya sınıf varyasyonunun statik yapıcısı çalıştırıldığında bilgileri almak için Yansıma kullanarak sağlanabilir. İkinci yaklaşımın Karşılaştırıcı <T> .Default () gibi şeyler tarafından kullanıldığına inanıyorum.
  2. İlgilenilen her sınıf T için, IGetWhateverClassInfo <T> uygulayan ve "yeni" bir kısıtlamayı karşılayan bir sınıf veya yapı tanımlayın. Sınıf aslında herhangi bir alan içermez, ancak tür bilgisiyle statik bir alan döndüren bir statik özelliğe sahiptir. Söz konusu sınıfın veya yapının türünü, bir örnek oluşturabilecek ve diğer sınıf hakkında bilgi almak için kullanabilecek söz konusu genel rutine aktarın. Bu amaçla bir sınıf kullanırsanız, her seferinde yeni bir tanımlayıcı nesne örneği oluşturmak zorunda kalmamak için muhtemelen yukarıda belirtildiği gibi statik bir genel sınıf tanımlamanız gerekir. Bir yapı kullanırsanız, örnekleme maliyeti sıfır olmalıdır, ancak her farklı yapı türü DoSomething yordamının farklı bir şekilde genişletilmesini gerektirir.

Bu yaklaşımların hiçbiri gerçekten çekici değil. Öte yandan, CLR'de bu tür bir işlevselliği temiz bir şekilde sağlamak için var olan mekanizmalar varsa, .net'in parametreli "yeni" kısıtlamalar belirtmesine izin vereceğini beklerdim (çünkü bir sınıfın belirli bir imzası olan bir kurucuya sahip olup olmadığını bilmek belirli bir imzayla statik bir yönteme sahip olup olmadığını bilmekte güçlükle karşılaştırılabilir olması).


16

Kısa görüşlülük, sanırım.

Başlangıçta tasarlandığında, arayüzlerin sadece sınıf örnekleri ile kullanılması amaçlanmıştır.

IMyInterface val = GetObjectImplementingIMyInterface();
val.SomeThingDefinedinInterface();

Sadece arayüzlerin tanıtımı ile oldu, çünkü jenerikler için kısıtlamalar bir arayüze statik bir yöntem ekleyerek pratik bir kullanıma sahipti.

(yoruma cevap) :) Şimdi değiştirmenin CLR'de mevcut meclislerle uyumsuzluğa yol açacak bir değişiklik gerektirdiğine inanıyorum.


Bu sorunla ilk olarak karşılaştığım jenerikler bağlamında, ancak arabirimlere statik yöntemler eklemenin diğer bağlamlarda da yararlı olup olmadığını merak ediyorum? İşlerin değiştirilememesinin bir nedeni var mı?
Kramii

Ben de bazı parametrelerle kendini parametre türünü gerektiren bir genel sınıf uygularken bu karşılaşma. Çünkü new () hiçbirini alamaz. Bunu nasıl yapacağınızı henüz anladınız mı, Kramii?
Tom

1
@Kramii: Statik API'ler için sözleşmeler. Bir nesne örneği istemiyorum, sadece belirli bir imzanın garantisi gibi. IMatrixMultiplier veya ICustomSerializer. Sınıf üyeleri olarak işlevler / Eylemler / Delegeler hile yapar, ancak IMO bu bazen aşırıya kaçmış gibi görünür ve API'yi genişletmeye çalışmak için deneyimsiz olanlar için kafa karıştırıcı olabilir.
David Cuccia

14

Arabirimler bir nesnenin davranışını belirtir.

Statik yöntemler bir nesnenin davranışını değil, bir nesneyi bir şekilde etkileyen davranışı belirtir.


71
Üzgünüm .. Bunun doğru olduğundan emin değilim! Bir arayüz davranış belirtmez. Bir arabirim, adlandırılmış işlemler kümesini tanımlar. İki sınıf, tamamen farklı şekillerde davranmak için bir arabirimin yöntemini uygulayabilir. Dolayısıyla, bir arabirim hiçbir şekilde davranış belirtmez. Uygulayan sınıf yapar.
Scott Langham

1
Umarım seçici olduğumu düşünmüyorsun .. ama bence OO öğrenen herkesin anlaması önemli bir ayrım.
Scott Langham

4
Bir arabirimin, davranış ve sunum içeren bir sözleşme belirtmesi gerekir. Bu nedenle, her ikisinin de düzeltilmesi gerektiği için bir arabirim çağrısının davranışını değiştirmek hayır. Eğer çağrının farklı davrandığı bir arayüzünüz varsa (yani IList.Add bir kaldırma yaptı) doğru olmaz.
Jeff Yates

14
Evet, kafasına, ismiyle uyumsuz davranmak için bir yöntem tanımlamak için çarpıtılmış olacaksınız. Eğer IAlertService.GetAssistance () varsa, bir ışık yanıp sönmesi, bir alarm çalması veya bir sopayla göze atmak olabilir.
Scott Langham

1
Bir uygulama aynı zamanda bir günlük dosyasına yazabilir. Bu davranış arabirimde belirtilmedi. Ama, belki haklısın. 'Yardım alma' davranışına gerçekten saygı gösterilmelidir.
Scott Langham

14

Arayüzler "sözleşmeleri" temsil ettiği ölçüde, statik sınıfların arayüzleri uygulaması sessiz görünüyor.

Yukarıdaki argümanların hepsi sözleşmelerle ilgili bu noktayı kaçırıyor gibi görünüyor.


2
Bu basit ama etkili cevaba tamamen katılıyorum. "Statik arayüzde" ilginç olan şey, bir sözleşmeyi temsil etmesidir. Belki "statik arayüz" olarak adlandırılmamalıdır, ama yine de bir yapıyı özlüyoruz. Örneğin, .NET'in resmi belgesini ICustomMarshaler arabirimi hakkında kontrol edin. "GetInstance adı verilen ve bir String'i parametre olarak kabul eden ve dönüş türü ICustomMarshaler olan bir statik yöntem eklemek" için onu uygulayan sınıf gerektirir. C # tercih ederken ciddiye düz İngilizce bir "statik arayüz" tanımı gibi görünüyor ...
Simon Mourier

@SimonMourier Bu belgeler daha açık bir şekilde yazılmış olabilir, ancak yanlış yorumluyorsunuz. GetInstance statik yöntemini gerektiren ICustomMarshaler arabirimi değildir. Bunu gerektiren [MarshalAs] kod özelliğidir. Özniteliğin ekli marshaler'ın bir örneğini almasına izin vermek için fabrika modelini kullanıyorlar. Ne yazık ki, MarshalAs dokümantasyon sayfasında GetInstance gereksinimini belgelemeyi tamamen unuttular (sadece yerleşik marshaling uygulamalarını kullanan örnekleri gösterir).
Scott Gartner

@ScottGartner - Anlamını alma. msdn.microsoft.com/en-us/library/… açıkça şöyle ifade eder: "ICustomMarshaler arabirimini uygulamaya ek olarak, özel marshalers GetInstance adlı bir dizeyi parametre olarak kabul eden ve dönüş tipi ICustomMarshaler olan statik bir yöntem uygulamalıdır. Statik yöntem, ortak dil çalışma zamanının COM birlikte çalışma katmanı tarafından özel marshaler örneğini başlatmak için çağrılır. " Bu kesinlikle statik bir sözleşme tanımıdır.
Simon Mourier

9

Bir arabirimin amacı, polimorfizme izin vermek olduğundan, tanımlanmış arabirimi uygulamak için tanımlanmış herhangi bir sayıda tanımlanmış sınıfın bir örneğini iletebilmesidir ... polimorfik çağrınızda kodun bulabileceğini garanti eder aradığınız yöntem. arayüzü uygulamak için statik bir yönteme izin vermek hiç mantıklı değil,

Nasıl denir?


public interface MyInterface { void MyMethod(); }
public class MyClass: MyInterface
{
    public static void MyMethod() { //Do Something; }
}

 // inside of some other class ...  
 // How would you call the method on the interface ???
    MyClass.MyMethod();  // this calls the method normally 
                         // not through the interface...

    // This next fails you can't cast a classname to a different type... 
    // Only instances can be Cast to a different type...
    MyInterface myItf = MyClass as MyInterface;  

1
Diğer diller (örneğin Java), nesne örneklerinden statik yöntemlerin çağrılmasına izin verir, ancak bunları statik bir bağlamdan çağırmanız gerektiğine dair bir uyarı alırsınız.
Chris Marasti-Georg

.Net dosyasında bir örnekten statik bir yöntemin çağrılmasına izin verilir. Bu farklı bir şey. Statik bir yöntem bir arabirim uyguladıysa, bunu bir örnek OLMADAN çağırabilirsiniz. Mantıklı olmayan şey budur. Uygulamayı bir arabirime koyamayacağınızı, bunun sınıfta olması gerektiğini unutmayın. Peki bu arayüzü uygulamak için beş farklı sınıf tanımlanmışsa ve her biri bu statik yöntemin farklı bir uygulamasına sahip olsaydı, derleyici hangisini kullanırdı?
Charles Bretana

1
@CharlesBretana, jenerikler söz konusu olduğunda, geçilen tip için. Statik yöntemler için arayüzlere sahip olmanın çok yararlı olduğunu görüyorum (veya isterseniz, "statik arayüzler" diyelim ve bir sınıfın her iki statik arayüzü tanımlamasına izin verin ve örnek arayüzleri). Eğer varsa , bir örneğe ihtiyaç duymadan uygulama içinde Whatever<T>() where T:IMyStaticInterfacearayabilir Whatever<MyClass>()ve sahip olabilir . Çağrı yöntemi çalışma zamanında belirlenir. Bunu düşünerek yapabilirsiniz, ama zorlanacak bir “sözleşme” yoktur. T.MyStaticMethod()Whatever<T>()
Jcl

4

Jenerik olmayan bağlamlarda kullanılan statik yöntemlerle ilgili olarak, arayüzlerde bunlara izin vermenin pek mantıklı olmadığını kabul ediyorum, çünkü yine de arayüze bir referansınız olsaydı onları arayamazsınız. Bununla birlikte, polimorfik bağlamda değil, jenerik bir arayüzde arayüzler kullanılarak oluşturulan dil tasarımında temel bir delik vardır. Bu durumda arayüz hiç bir arayüz değil, bir kısıtlamadır. C # bir arayüz dışında bir kısıtlama kavramına sahip olmadığından, önemli işlevsellikten yoksundur. Konuşma konusu olan mesele:

T SumElements<T>(T initVal, T[] values)
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

Burada bir polimorfizm yoktur, jenerik nesnenin gerçek türünü kullanır ve + = operatörünü çağırır, ancak bu başarısız olur, çünkü bu operatörün var olduğundan emin olamaz. Basit çözüm, kısıtlamada belirtmektir; basit çözüm imkansızdır çünkü operatörler statiktir ve statik yöntemler bir arayüzde olamaz ve (burada sorun) kısıtlamalar arayüz olarak gösterilir.

C # gereken gerçek bir kısıtlama türüdür, tüm arayüzler de kısıtlamalar olacaktır, ancak tüm kısıtlamalar arayüzler olmayacaktır, o zaman bunu yapabilirsiniz:

constraint CHasPlusEquals
{
    static CHasPlusEquals operator + (CHasPlusEquals a, CHasPlusEquals b);
}

T SumElements<T>(T initVal, T[] values) where T : CHasPlusEquals
{
    foreach (var v in values)
    {
        initVal += v;
    }
}

Uygulanacak tüm sayısal türler için bir IAritmetik yapma hakkında çok fazla konuşma yapıldı, ancak verimlilikle ilgili endişeler var, çünkü bir kısıtlama polimorfik bir yapı değil, CArithmetic bir kısıtlama yapmak bu sorunu çözecektir.


C # 'daki operatörler statiktir, bu yüzden bu hala mantıklı değildir.
John Saunders

Buradaki sınırlama, TYPE (örnek değil) + işlecine (ve + + işleci tarafından) sahip olduğunu doğrular ve şablonun oluşturulmasına izin verir, ardından gerçek şablon ikamesi meydana geldiğinde, kullanılan nesnenin gerçekleşmesi garanti edilir işleci (veya başka bir statik yöntemi) içeren bir tür. Yani şunu söyleyebilirsiniz: SumElements <int> (0, 5); ki bunu başlatır: SumElements (int initVal, int [] değerler) {foreach (değerlerde var v) {initVal + = v; }}, elbette, mantıklı
Jeremy Sorensen

1
Unutmayın, bu bir şablon değildir. C ++ şablonlarıyla aynı olmayan jenerikler.
John Saunders

@JohnSaunders: Jenerik şablonları değildir ve onlar makul statik olarak bağlı operatörleriyle çalışmalarına yapılmış olabilir bilmiyorum ama genel bağlamlarda bir belirtmek için yararlı olabilir birçok durumlar vardır Tbir olmalıdır statik üye (örneğin, Törnek üreten bir fabrika ). Çalışma zamanı değişiklikleri olmasa bile, dillerin sözdizimsel şeker gibi bir şeyi etkili ve birlikte çalışabilir bir şekilde uygulamasına izin verecek kuralları ve yardımcı yöntemleri tanımlamanın mümkün olacağını düşünüyorum. Sanal statik yöntemlerle her arabirim için bir yardımcı sınıf
olsaydı

1
@JohnSaunders: Sorun, yöntemin bir arabirim uyguladığı kadar değil, bunun yerine derleyicinin, seçimi temel alacak bir nesne örneği olmadan çağrılacak sanal bir yöntem seçememesi. Bir çözüm, "statik arabirim" çağrısını sanal dağıtım (işe yaramaz) kullanarak değil, genel bir statik sınıfa çağrı kullanmaktır. Tür tabanlı genel gönderim bir örneğe sahip olmayı gerektirmez, yalnızca bir türe sahip olmayı gerektirir.
supercat

3

Çünkü arayüzler kalıtım yapısındadır ve statik yöntemler iyi miras almaz.


3

İstediğiniz şey, hem Tür hem de o türün herhangi bir örneği aracılığıyla statik bir yöntemin çağrılmasına izin verir. Bu en azından arzu edilen bir özellik olmayan belirsizliğe yol açacaktır.

Bunun önemli olup olmadığı, hangisinin en iyi uygulama olduğu ve bunu bir şekilde yapan performans sorunlarının olup olmadığı konusunda sonsuz tartışmalar olacaktır. Sadece desteklemeyerek C # bizi endişelenmekten kurtarır.

Ayrıca, bu arzuya uyan bir compilier'ın örnek ve statik yöntemler arasında daha katı bir ayrımla gelebilecek bazı optimizasyonları kaybetmesi muhtemeldir.


İlginç bir şekilde, VB.Net'te her iki Tip reklam Örneği tarafından statik bir yöntem çağırmak oldukça mümkündür (IDE son durumda bir uyarı verse de). Bir sorun gibi görünmüyor. İyileştirmeler konusunda haklı olabilirsiniz.
Kramii

3

Bir sınıfın statik yöntemlerini ve statik olmayan yöntemlerini farklı arabirimler olarak düşünebilirsiniz. Çağrıldığında, statik yöntemler tekli statik sınıf nesnesine, statik olmayan yöntemler ise ele aldığınız sınıf örneğine çözümlenir. Bu nedenle, bir arabirimde statik ve statik olmayan yöntemler kullanırsanız, gerçekten bir uyumlu şeye erişmek için arabirimlerin kullanılmasını istediğimizde etkili bir şekilde iki arabirim bildirirsiniz.


Bu ilginç bir POV ve muhtemelen C # tasarımcılarının aklında olan. Artık statik üyeleri farklı bir şekilde düşüneceğim.
Kramii

3

Arabirim yöntemlerinin statik uygulamasını ya da Mark Brackett'in "sözde tür yöntemi" olarak ne tanıttığını eksik bir örnek vermek için:

Bir veritabanı deposundan okurken, herhangi bir yapının tablosundan okumayı işleyen genel bir DataTable sınıfımız vardır. Tüm tabloya özgü bilgiler, DB'den bir satır için de verileri tutan ve bir IDataRow arabirimi uygulaması gereken tablo başına bir sınıfa konur. IDataRow, veritabanından okunacak tablonun yapısının bir açıklamasıdır. DataTable, DB'den okumadan önce IDataRow veri yapısını istemelidir. Şu anda şöyle görünüyor:

interface IDataRow {
  string GetDataSTructre();  // How to read data from the DB
  void Read(IDBDataRow);     // How to populate this datarow from DB data
}

public class DataTable<T> : List<T> where T : IDataRow {

  public string GetDataStructure()
    // Desired: Static or Type method:
    // return (T.GetDataStructure());
    // Required: Instantiate a new class:
    return (new T().GetDataStructure());
  }

}

GetDataStructure, her tablonun okuması için yalnızca bir kez gereklidir, bir örnek daha başlatmak için ek yük en az düzeydedir. Ancak, bu durumda burada iyi olurdu.


1

Bilginize: Arayüz için uzantı yöntemleri oluşturarak, istediğiniz şeye benzer bir davranış elde edebilirsiniz. Genişletme yöntemi paylaşılan, geçersiz kılınamayan statik bir davranış olacaktır. Ancak, ne yazık ki, bu statik yöntem sözleşmenin bir parçası olmayacaktır.


1

Arayüzler, tanımlanmış mevcut işlevselliğin soyut kümeleridir.

Bu arabirimdeki bir yöntemin statik olarak davranıp davranmayacağı, arabirimin arkasında gizlenmesi gereken bir uygulama ayrıntısıdır . Bir arabirim yöntemini statik olarak tanımlamak yanlış olur çünkü yöntemi gereksiz yere belirli bir şekilde uygulamaya zorlarsınız.

Yöntemler statik olarak tanımlandıysa, arabirimi uygulayan sınıf olabildiğince kapsüllenmez. Kapsülleme nesne yönelimli tasarım için çabalamak için iyi bir şeydir (nedenine girmeyeceğim, bunu buradan okuyabilirsiniz: http://en.wikipedia.org/wiki/Object-oriented ). Bu nedenle arabirimlerde statik yöntemlere izin verilmez.


1
Evet, arayüz bildirimindeki bir statik saçma olurdu. Bir sınıf arabirimi belirli bir şekilde uygulamaya zorlanmamalıdır. Ancak, C # bir sınıfı statik olmayan yöntemler kullanarak arabirimi uygulamakla sınırlandırır. Bu mantıklı mı? Neden?
Kramii

'Statik' anahtar kelimesini kullanamazsınız. Ancak statik olarak davranan bir yöntem yazmak için statik anahtar kelimeye ihtiyacınız olmadığından herhangi bir kısıtlama yoktur. Nesnenin üyelerinden hiçbirine erişmeyen bir yöntem yazın, o zaman tıpkı statik bir yöntem gibi davranacaktır. Yani bir kısıtlama var.
Scott Langham

Doğru Scott, ancak kodun başka bir bölümünde birisinin bu yönteme statik bir şekilde erişmesine izin vermez. İstemek için bir neden düşünebileceğimden değil, OP'nin istediği gibi görünüyor.
Chris Marasti-Georg

Gereksinimi gerçekten hissettiyseniz, kodun başka bir bölümüne erişmek için statik bir yöntem olarak yazabilir ve statik yöntemi çağırmak için statik olmayan yöntemi yazabilirsiniz. Yanılıyor olabilirim, ama bunun sorgulayıcının bir amacı olduğundan şüpheliyim.
Scott Langham

1

Statik sınıflar bunu yapabilir, böylece genel olarak kullanılabilirler. Bunun yerine istenen sonuçları elde etmek için bir Singleton uygulamak zorunda kaldım.

"Kullanıcı", "Takım", vb gibi her varlık türü için "Oluştur", "Oku", "Güncelle", "Sil" gibi CRUD yöntemlerini uygulayan bir grup Statik İş Katman sınıfları vardı. Sonra bir temel oluşturdum CRUD yöntemlerini uygulayan Business Layer sınıfı için soyut bir özelliğe sahip olan denetim. Bu, temel sınıftan "Oluştur", "Oku", "Güncelle", "Sil" işlemlerini otomatikleştirmemi sağladı. Statik sınırlama nedeniyle bir Singleton kullanmak zorunda kaldım.


1

Çoğu insan OOP Sınıfları da nesneler olduğunu unutmak gibi görünüyor, ve bu nedenle bazı nedenlerden dolayı c # "statik yöntem" olarak adlandırılan iletileri vardır. Örnek nesneler ve sınıf nesneleri arasındaki farklılıkların var olması, yalnızca dildeki kusurları veya eksiklikleri gösterir. C # hakkında iyimser ...


2
Sorun şu ki, bu soru "in OOP" ile ilgili değil. "C #" da. C # 'da "mesaj" yoktur.
John Saunders

1

Tamam burada bir 'tür yöntemi' gerekli bir örnek. Bazı kaynak XML tabanlı sınıflardan birini oluşturuyorum. Yani benim

  static public bool IsHandled(XElement xml)

her sınıfı sırayla çağırır.

İşlev statik olmalıdır, aksi takdirde uygunsuz nesneler oluşturmak için zaman harcarız. @Ian Boyde'nin belirttiği gibi, bir fabrika sınıfında yapılabilir, ancak bu sadece karmaşıklık katar.

Sınıf uygulayıcılarını uygulamaya zorlamak için arayüze eklemek güzel olurdu. Bu önemli bir ek yüke neden olmaz - sadece derleme / bağlantı zamanı kontrolüdür ve vtable'ı etkilemez.

Bununla birlikte, bu oldukça küçük bir gelişme olacaktır. Yöntem statik olduğundan, ben arayan olarak, açıkça çağırmak gerekir ve bu yüzden hemen uygulanmadıysa derleme hatası almak. Arayüzde belirtilmesine izin vermek, bu hatanın geliştirme döngüsünde marjinal olarak daha erken geldiği anlamına gelir, ancak bu diğer kırık arayüz sorunlarına kıyasla önemsizdir.

Bu nedenle, dengede muhtemelen en iyi durumda olan küçük bir potansiyel özelliktir.


1

Statik bir sınıfın, statik elemanlarla bir sınıfın özel bir örneğini oluşturarak Microsoft tarafından C # 'da uygulanmış olması, statik işlevselliğin nasıl elde edildiğinin tuhaflığıdır. Bu teorik bir nokta değil.

Bir arabirim, sınıf arabiriminin veya onunla nasıl etkileşime girdiğinin bir tanımlayıcısı OLMALIDIR ve statik olan etkileşimleri içermelidir. Arayüzün genel tanımı (Meriam-Webster'den): farklı şeylerin buluştuğu ve birbiriyle iletişim kurduğu veya etkilediği yer veya alan. Bir sınıfın veya statik sınıfların statik bileşenlerini tamamen atladığınızda, bu kötü çocukların nasıl etkileşime girdiğinin büyük bölümlerini görmezden geliriz.

Statik sınıflarla arayüzleri kullanabilmenin oldukça yararlı olacağına dair çok açık bir örnek:

public interface ICrudModel<T, Tk>
{
    Boolean Create(T obj);
    T Retrieve(Tk key);
    Boolean Update(T obj);
    Boolean Delete(T obj);
}

Şu anda, ben hiçbir şey unutmadım emin olmak için herhangi bir denetim olmadan bu yöntemleri içeren statik sınıfları yazmak. OOP'tan önceki kötü programlama günleri gibidir.


Bu eski bir soru; bu günlerde, görüşe dayalı soruları önlemek için çok çalışıyoruz çünkü yetkili olarak cevaplamaları zor. Buna cevap vermenin tek iyi yolu, MS'in bunu yaptığı gibi yapması veya açıkça göstermesi gereken nedenleri açıklamak olacaktır.
Nathan Tuggy

1

C # ve CLR, Java'nın yaptığı gibi arabirimlerdeki statik yöntemleri desteklemelidir. Statik değiştirici bir sözleşme tanımının bir parçasıdır ve özellikle davranış ve dönüş değerinin yine de çağrıdan çağrıya değişebilmesine rağmen örneğe göre değişmediği anlamına gelir.

Bununla birlikte, bir arabirimde statik bir yöntem kullanmak istediğinizde ve bunun yerine bir ek açıklama kullanmamanızı öneririz. Aradığınız işlevselliği alacaksınız.


0

Kısa cevap "sıfır kullanışlı olduğu için" olduğunu düşünüyorum. Bir arabirim yöntemini çağırmak için türün bir örneğine ihtiyacınız vardır. Örnek yöntemlerden istediğiniz statik yöntemleri çağırabilirsiniz.


0

Soru, C # başka bir anahtar kelime, tam olarak bu tür bir durum için ihtiyacı olduğunu düşünüyorum. Dönüş değeri yalnızca çağrıldığı türe bağlı olan bir yöntem istiyorsunuz. Adı geçen tür bilinmiyorsa buna "statik" diyemezsiniz. Ancak tür bir kez bilindiğinde statik hale gelir. "Çözülmemiş statik" fikirdir - henüz statik değildir, ancak alıcı tipini öğrendikten sonra, öyle olacaktır. Bu oldukça iyi bir kavram, bu yüzden programcılar bunu istiyorlar. Ancak tasarımcıların dil hakkında düşünme şekline pek uymadı.

Kullanılabilir olmadığından, aşağıda gösterilen şekilde statik olmayan yöntemleri kullanmaya başladım. Tam olarak ideal değil, ama en azından benim için değil, daha anlamlı bir yaklaşım göremiyorum.

public interface IZeroWrapper<TNumber> {
  TNumber Zero {get;}
}

public class DoubleWrapper: IZeroWrapper<double> {
  public double Zero { get { return 0; } }
}

0

Nesneye yönelik kavramlara göre Sınıflar tarafından uygulanan arabirim ve nesne kullanılarak bu uygulanan işleve (veya yöntemlere) erişmek için sözleşmesi vardır.

Dolayısıyla, Interface Contract yöntemlerine erişmek istiyorsanız nesne oluşturmanız gerekir. Statik yöntemlerde her zaman izin verilmez. Statik sınıflar, yöntem ve değişkenler hiçbir zaman nesneye ihtiyaç duymaz ve o alanın (veya sınıfın) nesnesi oluşturulmadan belleğe yüklenmez veya Nesne Oluşturma gerektirmediğini söyleyebilirsiniz.


0

kavramsal olarak , bir arabirimin statik yöntemler içeren bir sözleşme tanımlamamasının bir nedeni yoktur.

Mevcut C # dili uygulaması için kısıtlama, bir temel sınıfın ve arabirimlerin mirasının ödenmesine bağlıdır. "Class SomeBaseClass", "ISomeInterface" arabirimini ve "SomeDerivedClass sınıfı" arabirimini uygularsa, SomeBaseClass, ISomeInterface "arabirimi de uygularsa, bir arabirim yöntemini uygulamak için statik bir yöntem derleme başarısız olur çünkü statik yöntem bir örnek yöntemiyle aynı imzayı ( arayüzü uygulamak için temel sınıfta bulunmalıdır).

Statik bir sınıf işlevsel olarak bir tektona özdeştir ve daha temiz sözdizimine sahip bir tektonla aynı amaca hizmet eder. Bir singleton bir arabirim uygulayabildiğinden, statikle arabirim uygulamaları kavramsal olarak geçerlidir.

Bu nedenle, örneğin, C # ad çakışması sınırlaması ve miras boyunca aynı ada sahip statik yöntemler sınırlanır. C # 'ın statik yöntem sözleşmelerini (arayüzler) desteklemek için "yükseltilememesi" için bir neden yoktur.


-1

Bir sınıf bir arabirim uyguladığında, arabirim üyeleri için örnek oluşturur. Statik türün bir örneği olmasa da, arabirimde statik imzalara sahip olmanın bir anlamı yoktur.

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.