Neden statik sınıfları devralamıyorum?


224

Gerçekten herhangi bir duruma ihtiyaç duymayan birkaç dersim var. Örgütsel açıdan, onları hiyerarşiye koymak istiyorum.

Ama öyle görünüyor ki statik sınıflar için miras beyan edemem.

Bunun gibi bir şey:

public static class Base
{
}

public static class Inherited : Base
{
}

çalışmayacak.

Dil tasarımcıları bu olasılığı neden kapattı?


Yanıtlar:


174

Buradan alıntı :

Bu aslında tasarım gereğidir. Statik bir sınıfı devralmak için iyi bir neden yok gibi görünüyor. Her zaman sınıf adının kendisi aracılığıyla erişebileceğiniz genel statik üyelere sahiptir. Statik şeyleri miras almak için gördüğüm tek neden, birkaç karakter yazmak gibi kötü şeylerdi.

Statik üyeleri doğrudan kapsama getirmek için mekanizmaları düşünmek için bir neden olabilir (ve aslında bunu Orcas ürün döngüsünden sonra ele alacağız), ancak statik sınıf mirası gitmenin yolu değildir: Kullanmak için yanlış mekanizmadır ve çalışır yalnızca statik bir sınıfta bulunan statik üyeler için.

(Mads Torgersen, C # Dil Yöneticisi)

Kanal9'dan diğer görüşler

.NET'te devralma yalnızca örnek tabanında çalışır. Statik yöntemler, örnek düzeyinde değil tür düzeyinde tanımlanır. Bu nedenle geçersiz kılma statik yöntemlerle / özelliklerle / olaylarla çalışmaz ...

Statik yöntemler yalnızca bir kez bellekte tutulur. Onlar için oluşturulan sanal tablo vb. Yoktur.

.NET'te bir örnek yöntemini çağırırsanız, her zaman geçerli örneği verirsiniz. Bu, .NET çalışma zamanı tarafından gizlenir, ancak olur. Her örnek yönteminin, ilk olarak, yöntemin çalıştırıldığı nesneye yönelik bir işaretçisi (başvuru) vardır. Bu statik yöntemlerle (tip düzeyinde tanımlandıkları için) gerçekleşmez. Derleyici çalıştırılacak yöntemi seçmeye nasıl karar vermelidir?

(Littleguru)

Ve değerli bir fikir olarak, littleguru'nun bu sorun için kısmi bir "geçici çözümü" vardır: Singleton deseni.


92
Torgersen'in tarafında hayal gücü eksikliği, gerçekten. Bunu yapmak için oldukça iyi bir nedenim vardı :)
Thorarin

10
Buna ne dersin? Açık kaynak olmayan bir dll'niz var / kaynağına erişiminiz yok, diyelim NUnit. Atar <> (Eylem a) gibi bir yönteme sahip olmak için Assert sınıfını genişletmek istiyorsunuz (evet, orada da var, ama bir noktada yoktu.) Projenize bir Assert: NUnit.Framework.Assert ekleyebilirsiniz. yazın ve bir Throws yöntemi ekleyin. Sorun çözüldü. Kodda da kullanmak daha temiz ... başka bir sınıf adı bulmak ve bu sınıf adını hatırlamak yerine Assert yazın. ve mevcut yöntemlere bakın.
user420667

4
@KonradMorawski Statik sınıflar için genişletme yöntemleri yazmak mümkün değildir. stackoverflow.com/questions/249222/…
Martin Braun

2
Tabii ki modiX. Beni burada yanlış anladın. Demek istediğim, user420667 tarafından açıklanan senaryoda gerekli işlevsellik ( gelecekte ) statik sınıflar için genişletme yöntemlerine izin verdiği kadar az sağlanabiliyor . Statik kalıtım gerekmez. Bu yüzden senaryosu bana statik miras getirme konusunda çok iyi bir gerekçe göstermedi. C # bu açıdan değiştirilecekse, neden küçük bir uygulamada iyi olduğu zaman büyük bir yeniden tasarım yapalım.
Konrad Morawski

5
@ Öğrenci Bu gerçekten adil değil. Net'te her şey miras alır objectve herkesin bunu bilmesi beklenir. Dolayısıyla, statik sınıflar , açıkça belirtilseniz de belirtmeseniz de her zaman miras alır object.
RenniePet

71

Statik bir sınıfı devralmamanızın ana nedeni, soyut ve mühürlenmiş olmalarıdır (bu aynı zamanda herhangi bir örneğinin oluşturulmasını da önler).

Yani bu:

static class Foo { }

bu IL'yi derler:

.class private abstract auto ansi sealed beforefieldinit Foo
  extends [mscorlib]System.Object
 {
 }

17
Statikin soyut + mühürlü olarak uygulandığını söylüyorsunuz. Bunun neden yapıldığını bilmek istiyor . Neden mühürlendi?
Lucas

22
Bu her şeye cevap vermiyor; sadece sorduğu konuyu yeniden ifade ediyor.
Igby Largeman

28
Eh, OP elbette Cevabı olan, olmayan "Neden statik sınıflardan devralamaz?" "Neden statik sınıflar mühürlendi" sormalıydık "Onlar mühürlü çünkü" . Bu cevap benim oyumu alır.
Alex Budovski

6
statik sınıfların otomatik olarak kapalı sınıflar olarak derlendiğini paylaştığınız için teşekkürler - bunun nasıl / ne zaman olduğunu merak ediyordum!
Mark

23
@AlexBudovski: Bu cevap doğru ama kayıp bir kişiye "önümdasın" derken size "neredeyim" diye sorduğunda işe yaramaz.
DeepSpace101

24

Şöyle düşünün: statik üyelere aşağıdaki ad türüyle erişirsiniz:

MyStaticType.MyStaticMember();

Bu sınıftan miras almanız durumunda, sınıfa yeni tür adıyla erişmeniz gerekir:

MyNewType.MyStaticMember();

Bu nedenle, yeni öğe kodda kullanıldığında orijinal ile hiçbir ilişkisi yoktur. Çok biçimlilik gibi şeyler için herhangi bir miras ilişkisinden faydalanmanın bir yolu olmayacaktır.

Belki de sadece orijinal sınıftaki bazı öğeleri genişletmek istediğinizi düşünüyorsunuz. Bu durumda, orijinalin bir üyesini tamamen yeni bir tipte kullanmanızı engelleyen hiçbir şey yoktur.

Belki de mevcut bir statik türe yöntemler eklemek istersiniz. Bunu zaten uzatma yöntemleri ile yapabilirsiniz.

Belki de Typeçalışma zamanında bir işleve bir statik iletmek ve yöntemin tam olarak ne yaptığını bilmeden bu türdeki bir yöntemi çağırmak isteyebilirsiniz . Bu durumda, bir Arayüz kullanabilirsiniz.

Böylece, sonunda statik sınıfları miras almaktan hiçbir şey kazanmazsınız.


5
Varolan bir statik türe bir uzantı yöntemi aracılığıyla yöntem ekleyemezsiniz. İstenen bir örnek kullanım için lütfen kabul edilen cevap hakkındaki yorumuma bakın. (Temel olarak MyNewType adını verdiğiniz adı MyStaticType olarak yeniden adlandırmak istersiniz, bu nedenle MyStaticType: OldProject.MyStaticType)
user420667 11:11

2
Statik tipler ve sanal statik üyeler ile kalıtım ilişkileri, a'nın üyelerine SomeClass<T> where T:Fooerişebilecek şekilde tanımlanabilir Foove eğer Tbazı sanal statik üyelerini Foogeçersiz kılan bir sınıf olsaydı, genel sınıf bu geçersiz kılmaları kullanırdı. Muhtemelen, dillerin bunu mevcut CLR ile uyumlu bir şekilde yapabileceği bir kural tanımlamak bile mümkün olacaktır (örneğin, bu tür üyelere sahip bir sınıf, statik bir alanla birlikte bu tür örnek üyeleri içeren korumalı statik olmayan bir sınıf tanımlamalıdır bu tür bir örneği tutan).
supercat

Kendimi, statik bir sınıfı miras almanın doğru bir şey olduğuna inandığım bir dava ile buldum, ancak onlara yine de erişebilirsiniz. Yazıcıya ham veri akışları göndermek için statik bir sınıfım var (endüstriyel etiket yazıcılarıyla konuşmak için). Bir grup Windows API bildirimi vardır. Şimdi kendimi aynı API çağrıları gerektiren yazıcılarla uğraşmak için başka bir sınıf yazarken buluyorum. Birleştirin? İyi değil. Onları iki kez mi ilan ettiniz? Çirkin. Devralma? İyi, ama izin verilmiyor.
Loren Pechtel

3

Sınıf hiyerarşisini kullanarak elde etmek istediklerinize yalnızca isim aralığı ile ulaşılabilir. Dolayısıyla, ad boşluklarını destekleyen diller (C # gibi), statik sınıfların sınıf hiyerarşisini uygulamada hiçbir fayda sağlamaz. Sınıflardan hiçbirini başlatamadığınız için, tek ihtiyacınız olan, ad alanlarını kullanarak elde edebileceğiniz sınıf tanımlarının hiyerarşik bir organizasyonu.


Sınıf olarak bir sınıf alan genel bir yükleyici var. Bu sınıf 5 yardımcı yöntem ve bir dize (ad ve açıklama) döndüren 2 yöntem içerir. Yanlış seçmiş olabilirim (sanırım), ama bulduğum tek çözüm, genel yükleyicideki sınıfı başlatmak, yöntemlere erişmekti ... meh.
ANeves

Alternatif dev bir sözlük olurdu sanırım. Ayrıca, "ne elde etmek istediğinizi" söylediğinizde, bunun yerine "neyi hedeflediğinizi" veya benzer bir şey söylemelisiniz.
ANeves

3

Bunun yerine kompozisyon kullanabilirsiniz ... bu, statik nesneden sınıf nesnelerine erişmenizi sağlar. Ama hala arayüzleri veya soyut sınıfları uygulayamıyor


2

Miras alınan sınıflar adıyla "miras alınan" statik üyelere erişebilmenize rağmen, statik üyeler gerçekten miras alınmaz. Bu kısmen sanal ya da soyut olamazlar ve geçersiz kılınamazlar. Örneğinizde, bir Base.Method () bildirdiyseniz, derleyici yine de Inherited.Method () öğesine bir çağrıyı yine Base.Method () ile eşler. Base.Method () 'u da açıkça çağırabilirsiniz. Reflektör ile küçük bir test yazabilir ve sonucu görebilirsiniz.

Yani ... statik üyeleri devralamıyorsanız ve statik sınıflar yalnızca statik üyeler içeriyorsa , statik sınıfı devralmak ne işe yarar?


1

Hmmm ... statik yöntemlerle dolu statik olmayan sınıflarınız olsaydı çok farklı olurdu ..?


2
Bana kalan bu. Bunu bu şekilde yapıyorum. Ve hoşuma gitmedi.
Kullanıcı

Bunu da bu şekilde yaptım, ama bir sebepten dolayı nesneyi asla başlatamayacağınızı bildiğinizde estetik hissetmiyor. Maddi bir maliyeti olmamasına rağmen, bunun gibi detaylar hakkında her zaman acı çekiyorum.
gdbj

Statik yöntemlerle dolu statik olmayan sınıfta uzatma yöntemleri koyamazsınız :)
Jakub Szułakiewicz

1

Yapabileceğiniz bir geçici çözüm, statik sınıflar kullanmak değil, sınıfları statik üyelerin sınıf dışında erişilebilen tek şey olması için yapıcıyı gizlemektir. Sonuç, kalıtsal bir "statik" sınıftır:

public class TestClass<T>
{
    protected TestClass()
    { }

    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public class TestClass : TestClass<double>
{
    // Inherited classes will also need to have protected constructors to prevent people from creating instances of them.
    protected TestClass()
    { }
}

TestClass.Add(3.0, 4.0)
TestClass<int>.Add(3, 4)

// Creating a class instance is not allowed because the constructors are inaccessible.
// new TestClass();
// new TestClass<int>();

Ne yazık ki, "tasarım gereği" dil sınırlaması nedeniyle yapamayız:

public static class TestClass<T>
{
    public static T Add(T x, T y)
    {
        return (dynamic)x + (dynamic)y;
    }
}

public static class TestClass : TestClass<double>
{
}

1

Statik kalıtım gibi görünecek bir şey yapabilirsiniz.

İşte hile:

public abstract class StaticBase<TSuccessor>
    where TSuccessor : StaticBase<TSuccessor>, new()
{
    protected static readonly TSuccessor Instance = new TSuccessor();
}

Sonra bunu yapabilirsiniz:

public class Base : StaticBase<Base>
{
    public Base()
    {
    }

    public void MethodA()
    {
    }
}

public class Inherited : Base
{
    private Inherited()
    {
    }

    public new static void MethodA()
    {
        Instance.MethodA();
    }
}

InheritedSınıf kendisi statik değildir, ama biz bunu oluşturmak için izin vermez. Aslında inşa eden statik yapıcı Baseve Basestatik olarak kullanılabilir tüm özellikleri ve yöntemleri var . Artık tek yapmanız gereken, statik bağlamınıza göstermeniz gereken her yöntem ve özellik için statik sarmalayıcılar yapmak.

Statik sarma yöntemlerinin ve newanahtar kelimenin manuel olarak oluşturulması ihtiyacı gibi dezavantajlar vardır . Ancak bu yaklaşım, statik mirasa gerçekten benzeyen bir şeyi desteklemeye yardımcı olur.

PS Bunu derlenmiş sorgular oluşturmak için kullandık ve bu aslında ConcurrentDictionary ile değiştirilebilir, ancak iş parçacığı güvenliği ile statik bir salt okunur alan yeterince iyiydi.


1

Cevabım: kötü tasarım seçimi. ;-)

Bu sözdizimi etkisine odaklanan ilginç bir tartışmadır. Bence argümanın özü, tasarım kararının kapalı statik sınıflara yol açmasıdır. Statik sınıfın adlarının alt adların arkasına saklanmak ('kafa karıştırıcı') yerine en üst düzeyde görünen şeffaflığına mı odaklanılıyor? Üs veya çocuğa doğrudan erişebilen bir dil uygulaması kafa karıştırıcı olabilir.

Bir şekilde statik kalıtım olduğunu varsayarak sahte bir örnek tanımlandı.

public static class MyStaticBase
{
    SomeType AttributeBase;
}

public static class MyStaticChild : MyStaticBase
{
    SomeType AttributeChild;
}

Şunlara yol açar:

 // ...
 DoSomethingTo(MyStaticBase.AttributeBase);
// ...

aynı depolama alanını etkileyebilir (?)

// ...
DoSomethingTo(MyStaticChild.AttributeBase);
// ...

Çok kafa karıştırıcı!

Fakat bekle! Derleyici, her ikisinde de aynı imzayı taşıyan MyStaticBase ve MyStaticChild ile nasıl başa çıkacaktı? Çocuk yukarıdaki örneğimi geçersiz kılarsa, aynı depolamayı DEĞİŞTİRMİYOR, belki? Bu daha da karışıklığa yol açar.

Sınırlı statik kalıtım için güçlü bir bilgi alanı gerekçesi olduğuna inanıyorum. Kısaca sınırlar hakkında daha fazla bilgi. Bu sözde kod değeri gösterir:

public static class MyStaticBase<T>
{
   public static T Payload;
   public static void Load(StorageSpecs);
   public static void Save(StorageSpecs);
   public static SomeType AttributeBase
   public static SomeType MethodBase(){/*...*/};
}

Sonra alırsınız:

public static class MyStaticChild : MyStaticBase<MyChildPlayloadType>
{
   public static SomeType AttributeChild;
   public static SomeType SomeChildMethod(){/*...*/};
   // No need to create the PlayLoad, Load(), and Save().
   // You, 'should' be prevented from creating them, more on this in a sec...
} 

Kullanım gibi görünüyor:

// ...
MyStaticChild.Load(FileNamePath);
MyStaticChild.Save(FileNamePath);
doSomeThing(MyStaticChild.Payload.Attribute);
doSomething(MyStaticChild.AttributeBase);
doSomeThing(MyStaticChild.AttributeChild);
// ...

Statik çocuğu oluşturan kişinin, platformun veya ortamın serileştirme motoruna getirilebilecek herhangi bir sınırlamayı anladığı sürece serileştirme süreci hakkında düşünmesi gerekmez.

Statikler (tekiltonlar ve diğer 'küreseller' formları) genellikle yapılandırma depolaması etrafında gelir. Statik miras, bu tür bir sorumluluk tahsisinin, bir yapılandırma hiyerarşisiyle eşleşmesi için sözdiziminde temiz bir şekilde temsil edilmesine izin verecektir. Gösterdiğim gibi, temel statik kalıtım kavramları uygulanırsa büyük belirsizlik potansiyeli vardır.

Doğru tasarım seçiminin belirli sınırlamalarla statik mirasa izin vermek olduğuna inanıyorum:

  1. Hiçbir şeyi geçersiz kılma yok. Çocuk temel öznitelikleri, alanları veya yöntemleri değiştiremez ... Aşırı yükleme, derleyicinin alt öğeyi ve tabanı sıralamasına izin veren bir imza farkı olduğu sürece tamam olmalıdır.
  2. Yalnızca genel statik tabanlara izin verin, genel olmayan statik bir tabandan devralamazsınız.

Aynı mağazayı genel bir başvuru ile değiştirmeye devam edebilirsiniz MyStaticBase<ChildPayload>.SomeBaseField. Ancak genel türün belirtilmesi gerektiğinden cesaretiniz kırılacaktır. Çocuk referans temizleyici olurdu da: MyStaticChild.SomeBaseField.

Bir derleyici yazarı değilim, bu yüzden bir derleyicide bu kısıtlamaları uygulamanın zorlukları hakkında bir şey eksik olup olmadığından emin değilim. Bununla birlikte, sınırlı statik kalıtım için bilgi alanı ihtiyacı olduğuna dair güçlü bir inancım var ve temel cevap, zayıf (veya aşırı basit) bir tasarım seçimi nedeniyle yapamayacağınızdır.


0

Statik sınıflar ve sınıf üyeleri, sınıfın bir örneği oluşturulmadan erişilebilen veriler ve işlevler oluşturmak için kullanılır. Statik sınıf üyeleri, herhangi bir nesne kimliğinden bağımsız olan veri ve davranışı ayırmak için kullanılabilir: nesneye ne olursa olsun veriler ve işlevler değişmez. Statik sınıflar, sınıfta nesne kimliğine bağlı hiçbir veri veya davranış olmadığında kullanılabilir.

Bir sınıf yalnızca statik üyeler içerdiğini belirten statik olarak bildirilebilir. Statik sınıf örnekleri oluşturmak için yeni anahtar sözcüğü kullanmak mümkün değildir. Sınıfı içeren program veya ad alanı yüklendiğinde, statik sınıflar .NET Framework ortak dil çalışma zamanı (CLR) tarafından otomatik olarak yüklenir.

Belirli bir nesneyle ilişkili olmayan yöntemleri içermek için statik bir sınıf kullanın. Örneğin, örnek veriler üzerinde etkili olmayan ve kodunuzdaki belirli bir nesneyle ilişkili olmayan bir dizi yöntem oluşturmak yaygın bir gereksinimdir. Bu yöntemleri tutmak için statik bir sınıf kullanabilirsiniz.

Statik bir sınıfın ana özellikleri şunlardır:

  1. Yalnızca statik üyeler içerirler.

  2. Onlar somutlaştırılamazlar.

  3. Mühürlüler.

  4. Örnek Oluşturucuları içeremezler (C # Programlama Kılavuzu).

Bu nedenle, statik bir sınıf oluşturmak temel olarak yalnızca statik üyeler ve özel bir kurucu içeren bir sınıf oluşturmakla aynıdır. Özel bir kurucu sınıfın somutlaştırılmasını engeller.

Statik bir sınıf kullanmanın avantajı, derleyicinin hiçbir örnek üyesinin yanlışlıkla eklenmediğinden emin olmak için kontrol edebilmesidir. Derleyici, bu sınıfın örneklerinin oluşturulamayacağını garanti eder.

Statik sınıflar mühürlenir ve bu nedenle miras alınamaz. Object dışında herhangi bir sınıftan miras alamazlar. Statik sınıflar bir örnek oluşturucu içeremez; ancak statik bir yapıcıya sahip olabilirler. Daha fazla bilgi için bkz. Statik Yapıcılar (C # Programlama Kılavuzu).


-1

Yalnızca statik üyeleri ve özel bir kurucu içeren statik bir sınıf oluşturduğumuzda, statik yapıcı sınıfın statik bir sınıfı devralmadığımız için somutlaştırılmasını engellemesinin tek nedeni. sınıf adını kullanarak statik sınıf Statik sınıfı miras almaya çalışın iyi bir fikir değildir.

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.