Neden C # soyut statik yöntemleri olamaz?


182

Son zamanlarda sağlayıcılarla epeyce çalışıyorum ve soyut bir statik yöntem olan soyut bir sınıfa sahip olmak istediğim ilginç bir durumla karşılaştım. Konuyla ilgili birkaç gönderi okudum ve bu bir anlam ifade etti, ancak güzel bir açıklama var mı?


3
İlerideki iyileştirmelere izin vermek için lütfen bunları açık bırakın.
Mark Biek

6
Soru, C # başka bir anahtar kelimeye 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ı.
William Jockusch

@WilliamJockusch alım türü ne anlama geliyor? BaseClass.StaticMethod () 'ı çağırırsam, BaseClass karar vermek için kullanabileceği tek türdür. Ancak bu düzeyde soyut olduğu için yöntem çözülemez. Bunun yerine DerivedClass.StaticMethod'u iyi çağırırsanız, temel sınıf ilgisizdir.
Martin Capodici

Temel sınıfta, yöntem çözümlenmez ve bunu kullanamazsınız. Türetilmiş bir türe veya bir nesneye ihtiyacınız vardır (bu da türetilmiş bir türe sahip olacaktır). BaseClassObject.Method () veya DerivedClass.Method () öğesini çağırabilirsiniz. BaseClass.Method () yöntemini çağıramazsınız çünkü bu size tür vermez.
William Jockusch

Yanıtlar:


157

Statik yöntemler bu şekilde somutlaştırılmaz , sadece nesne referansı olmadan kullanılabilirler.

Statik bir yönteme çağrı, bir nesne başvurusu aracılığıyla değil, sınıf adı aracılığıyla yapılır ve bunu çağırmak için Ara Dil (IL) kodu, soyut yöntemi, onu tanımlayan sınıfın adıyla çağırır; kullandığınız sınıf.

Bir örnek göstereyim.

Aşağıdaki kodla:

public class A
{
    public static void Test()
    {
    }
}

public class B : A
{
}

B.Test'i çağırırsanız, şöyle:

class Program
{
    static void Main(string[] args)
    {
        B.Test();
    }
}

Sonra Main yöntemi içindeki gerçek kod aşağıdaki gibidir:

.entrypoint
.maxstack 8
L0000: nop 
L0001: call void ConsoleApplication1.A::Test()
L0006: nop 
L0007: ret 

Gördüğünüz gibi, A.Test çağrısı yapılır, çünkü kodu bu şekilde yazabilseniz bile, onu tanımlayan A sınıfıdır ve B.Test'e değil.

Eğer olsaydı sınıf türlerini bir değişken bir tip değil bir nesneye atıfta yapabilirsiniz Delphi, olduğu gibi, (aynı zamanda ve kurucular) sanal ve dolayısıyla soyut statik yöntemleri için daha fazla kullanılmasını olurdu, ancak bunlar mevcut değildir ve dolayısıyla statik aramalar .NET'te sanal değildir.

IL tasarımcılarının kodun B.Test'i çağırması için derlenmesine izin verebileceğini ve çağrıyı zamanında çözmesini sağlayabileceğini anlıyorum, ancak yine de orada bir tür sınıf adı yazmak zorunda kalacağınız için sanal olmayacaktır.

Sanal yöntemler ve dolayısıyla soyut olanlar, yalnızca çalışma zamanında birçok farklı türde nesne içerebilen bir değişken kullandığınızda yararlıdır ve bu nedenle değişkende bulunan geçerli nesne için doğru yöntemi çağırmak istersiniz. Statik yöntemlerle yine de bir sınıf adından geçmeniz gerekir, bu nedenle çağrılacak kesin yöntem derleme zamanında bilinir çünkü değişemez ve değişmez.

Bu nedenle, sanal / soyut statik yöntemler .NET'te kullanılamaz.


4
C # 'da operatör aşırı yüklemesinin yapılma şekli ile birlikte, bu maalesef belirli bir operatör aşırı yükü için bir uygulama sağlamak için alt sınıflar gerektirme olasılığını ortadan kaldırır.
Chris Moschini

23
I tanımı olarak pek de kullanışlı bu cevabı bulmuyorum Test()içindedir Asoyut ve potansiyel tanımlanan olmaktan ziyade B\.

5
Genel tip parametreleri, etkili olmayan "tip" değişkenler olarak etkili bir şekilde davranır ve sanal statik yöntemler bu bağlamda faydalı olabilir. Örneğin, eğer biri vardı Carsanal bir statik ile türünü CreateFromDescriptiondaha sonra, bir kabul kod fabrika yöntemiyle Car-constrained genel tür Tdiyebiliriz T.CreateFromDescriptiontürde bir araba üretmek için T. Böyle bir yöntemi tanımlayan her tip, sanal "statik" yöntemleri tutan iç içe bir sınıf jenerikinin statik tekil bir örneğini taşıyorsa, böyle bir yapı CLR içinde oldukça iyi bir şekilde desteklenebilir.
supercat

45

Statik yöntemler miras alınamaz veya geçersiz kılınamaz ve bu yüzden soyut olamazlar. Statik yöntemler bir sınıfın örneğinde değil türünde tanımlandığından, bu tür üzerinde açıkça çağrılmalıdır. Bu nedenle, alt sınıftaki bir yöntemi çağırmak istediğinizde, onu çağırmak için adını kullanmanız gerekir. Bu kalıtımı ilgisiz kılar.

Bir an için statik yöntemleri devralabileceğinizi varsayın. Bu senaryoyu düşünün:

public static class Base
{
    public static virtual int GetNumber() { return 5; }
}

public static class Child1 : Base
{
    public static override int GetNumber() { return 1; }
}

public static class Child2 : Base
{
    public static override int GetNumber() { return 2; }
}

Base.GetNumber () öğesini çağırırsanız, hangi yöntem çağrılır? Hangi değer geri döndü? Nesnelerin örnekleri oluşturmadan kalıtımın oldukça zor olduğunu görmek oldukça kolaydır. Mirassız soyut yöntemler sadece bedeni olmayan yöntemlerdir, bu yüzden çağrılamazlar.


33
Senaryonuz göz önüne alındığında Base.GetNumber () 5 döneceğini söyleyebilirim; Child1.GetNumber () işlevi 1 değerini döndürür; Child2.GetNumber () işlevi 2 değerini döndürür; Akıl yürütmenizi anlamama yardımcı olmak için beni yanlış kanıtlayabilir misiniz? Teşekkür ederim
Luis Filipe

Base.GetNumber () işlevinin 5 döndürdüğünü düşündüğünüz yüz, neler olduğunu zaten anladığınız anlamına gelir. Taban değeri döndürüldüğünde, herhangi bir miras kalmaz.
David Wengier

60
Neden dünyada Base.GetNumber () 5 dışında bir şey döndürür? Temel sınıftaki bir yöntem - orada sadece 1 seçenek var.
Artem Russakovskii

4
@ArtemRussakovskii: Varsayalım int DoSomething<T>() where T:Base {return T.GetNumber();}. DoSomething<Base>()Beş, DoSomething<Child2>()iki ise geri dönecek olsaydı faydalı olurdu. Bu yetenek sadece oyuncak örnekleri için değil, aynı zamanda class Car {public static virtual Car Build(PurchaseOrder PO);}türetilmiş her sınıfın Carbir satın alma emri verilen bir örneği oluşturabilecek bir yöntem tanımlaması gerektiği gibi bir şey için de yararlı olacaktır.
Supercat

4
Statik olmayan kalıtımla tamamen aynı "problem" vardır.
Ark-kun

18

Başka bir katılımcı (McDowell), polimorfizmin sadece nesne örnekleri için çalıştığını söyledi. Nitelikli olmalı; sınıfları "Sınıf" veya "Metaclass" türünün örnekleri olarak gören diller vardır. Bu diller hem örnek hem de sınıf (statik) yöntemler için polimorfizmi destekler.

C #, önceki Java ve C ++ gibi, böyle bir dil değildir; staticAnahtar kelime yöntemi statik olarak bağlı ziyade dinamik sanal / tutulmasının belirtilmesi açıkça kullanılmaktadır.


9

İşte statik alanlar ve yöntemler için kesinlikle mirasa ihtiyaç duyulan bir durum:

abstract class Animal
{
  protected static string[] legs;

  static Animal() {
    legs=new string[0];
  }

  public static void printLegs()
  {
    foreach (string leg in legs) {
      print(leg);
    }
  }
}


class Human: Animal
{
  static Human() {
    legs=new string[] {"left leg", "right leg"};
  }
}


class Dog: Animal
{
  static Dog() {
    legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"};
  }
}


public static void main() {
  Dog.printLegs();
  Human.printLegs();
}


//what is the output?
//does each subclass get its own copy of the array "legs"?

4
Hayır, 'bacaklar' dizisinin yalnızca bir örneği var. Statik kurucuların hangi sırayla adlandırılacağını bilmediğiniz için çıktı belirsizdir (aslında temel sınıf statik kurucunun çağrılması için hiçbir garanti yoktur). 'İhtiyaç', 'arzunun' muhtemelen daha doğru olduğu oldukça mutlak bir terimdir.
Sam

legsstatik bir soyut özellik olmalıdır.
Little Endian

8

Önceki açıklamalara eklemek için, statik yöntem çağrıları derleme zamanında belirli bir yönteme bağlıdır , bu da polimorfik davranışı dışlar.


C # statik olarak yazılmıştır; polimorfik yöntemlere yapılan çağrıları da anladığım gibi derleme zamanında bağlı - yani çalışma sırasında hangi yöntemi çağırmak için CLR kalmaz.
Adam Tolley

Peki polimorfizm CLR'de tam olarak nasıl çalışıyor? Açıklamanız sanal yöntem gönderimini reddetti.
Rytmis

Bu, olabildiğince faydalı bir yorum değildir. Yararlı söylemi ('anladığım kadarıyla') davet ettim, belki biraz daha fazla içerik sağlayabileceğinizi düşünün - insanlar buraya gelip hakaret değil, cevap arıyorlar. Her ne kadar aynı şeyden suçlu olabilirim - gerçekten bir soru olarak yukarıdaki yorumu kastettim: C # derleme zamanında bu şeyleri değerlendirmek değil mi?
Adam Tolley

Özür dilerim, bir hakaret demek istemedim (biraz sinsice cevap vermeyi itiraf etmeme rağmen ;-). Sorum şu: Bu sınıflara sahipseniz: class Base {public virtual void Method (); } class Türetilmiş: Base {public override void Method (); } yazın ve şöyle yazın: Base instance = new Derived (); instance.Method (); çağrı sitesindeki derleme zamanı türü bilgileri, gerçek örnek bir Türetilmiş olduğunda Base örneğimize sahip olduğumuzdur. Böylece derleyici çağrılacak yöntemi tam olarak çözemez. Bunun yerine çalışma zamanını göndermesini söyleyen bir "callvirt" IL talimatı yayar ..
Rytmis

1
Teşekkürler dostum, bu bilgilendirici! Sanırım IL'ye dalışımı yeterince uzun zamandır yapıyorum, bana şans diliyorum.
Adam Tolley

5

Aslında statik yöntemleri (delphi'de) geçersiz kılıyoruz, biraz çirkin, ama ihtiyaçlarımız için iyi çalışıyor.

Bunu sınıfların sınıf örneği olmadan kullanılabilir nesnelerinin bir listesini alabilmesi için kullanıyoruz, örneğin, şöyle görünen bir yöntemimiz var:

class function AvailableObjects: string; override;
begin
  Result := 'Object1, Object2';
end; 

Bu çirkin ama gerekli, bu şekilde tüm sınıfların sadece mevcut nesneleri aramak için anlık hale getirilmesi yerine, sadece gerekli olanı başlatabiliriz.

Bu basit bir örnektir, ancak uygulamanın kendisi tüm sınıfları tek bir sunucuda ve sunucunun sahip olduğu her şeye ihtiyaç duymayabilecek ve asla bir nesne örneğine ihtiyaç duymayacak birden fazla farklı istemciye sahip bir istemci-sunucu uygulamasıdır.

Bu, her istemci için farklı bir sunucu uygulamasına sahip olmaktan çok daha kolaydır.

Umarım örnek açıktır.


0

Soyut yöntemler dolaylı olarak sanaldır. Soyut yöntemler bir örnek gerektirir, ancak statik yöntemlerin bir örneği yoktur. Yani, soyut bir sınıfta statik bir yöntem olabilir, sadece statik soyut (ya da soyut statik) olamaz.


1
-1 sanal yöntemlerin tasarım dışında bir örneğe ihtiyacı yoktur. Ve aslında soruyu, saptırmak kadar ele almazsınız.
FallenAvatar
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.