Statik yöntemler neden geçersiz kılınmamalıdır?


13

Bu sorunun cevabında genel fikir birliği, statik yöntemlerin geçersiz kılınmayacağı yönündeydi (ve bu nedenle C # 'daki statik fonksiyonlar sanal veya soyut olamaz). Yine de bu sadece C # 'da değildir; Java da bunu yasaklıyor ve C ++ da hoşuna gitmiyor. Ancak, bir alt sınıfta geçersiz kılmak istediğim birçok statik işlev örneğini düşünebilirim (örneğin, fabrika yöntemleri). Teoride, etraflarında dolaşmanın yolları olsa da, hiçbiri temiz veya basit değildir.

Statik fonksiyonlar neden geçersiz kılınmamalıdır?


4
Delphi, statik yöntemlere oldukça benzeyen ve geçersiz kılınabilen sınıf yöntemlerini destekler . selfSınıfın bir örneğine değil, sınıfa işaret eden bir işaretçi geçirilirler .
CodesInChaos

Statik bir tanım: değişimin olmaması. Neden geçersiz kılmak istediğiniz bir işlevi statik yaptığınızı merak ediyorum.
JeffO

4
@JeffO: İngilizce "statik" tanımının, statik üye işlevlerinin benzersiz semantiği ile hiçbir ilgisi yoktur.
Yörüngedeki Hafiflik Yarışları

5
Sorunuz, " statik yöntemler neden tek sanal dağıtım yerine statik olarak yazılmış dağıtım kullanmalıdır ?" Soruyu bu şekilde ifade ettiğinizde, kendisinin cevap verdiğini düşünüyorum.
Eric Lippert

1
@EricLippert Soru "C # neden sınıf yöntemleri yerine statik yöntemler sunuyor?" Şeklinde daha iyi ifade edilebilir, ancak yine de geçerli bir soru.
CodesInChaos

Yanıtlar:


13

Statik yöntemlerle, geçersiz kılma mekanizmasının uygun kontrolünü sağlamak için hiçbir nesne yoktur.

Normal sınıf / örnek sanal yöntem mekanizması, geçersiz kılmaların hassas şekilde ayarlanmasına aşağıdaki gibi izin verir: her gerçek nesne tam olarak bir sınıfın örneğidir. Bu sınıf geçersiz kılmaların davranışını belirler; sanal yöntemlerde her zaman ilk çatlağı alır. Daha sonra uygulanması için ana yöntemi doğru zamanda çağırmayı seçebilir. Daha sonra her bir üst yöntem, üst yöntemini çağırmak için sıra alır. Bu, nesne yöneliminin bilindiği kodun yeniden kullanılma kavramlarından birini gerçekleştiren hoş bir üst düzey çağırmalarla sonuçlanır. (Burada temel / süper sınıfların kodu nispeten karmaşık bir şekilde yeniden kullanılıyor; OOP'ta başka bir dikey kod yeniden kullanımı kavramı, aynı sınıfın birden çok nesnesine sahip olmaktır.)

Temel sınıflar çeşitli alt sınıflar tarafından yeniden kullanılabilir ve her biri rahatça bir arada bulunabilir. Kendi davranışlarını dikte eden nesneleri, barışçıl ve eşzamanlı olarak diğerleriyle birlikte varolan nesneleri somutlaştırmak için kullanılan her sınıf. İstemci, hangi davranışları istediğini ve bir nesneyi örneklemek ve istendiği gibi başkalarına iletmek için hangi sınıfı kullanacağını seçerek kontrol eder.

(Bu mükemmel bir mekanizma değildir, elbette desteklenmeyen yetenekleri her zaman tanımlayabilir, bu yüzden fabrika yöntemi ve bağımlılık enjeksiyonu gibi desenler üstte katmanlıdır.)

Yani, başka bir şey değiştirmeden statik için bir geçersiz kılma yeteneği yapacak olsaydık, geçersiz kılmaları sipariş etmekte zorluk çekerdik. Geçersiz kılmanın uygulanabilirliği için sınırlı bir bağlam tanımlamak zor olacaktır, bu nedenle geçersiz kılmayı nesnelerde olduğu gibi yerel olarak değil küresel olarak elde edersiniz. Davranışı değiştirecek örnek nesne yok. Birisi, başka bir sınıf tarafından geçersiz kılınmış olan statik yöntemi çağırdıysa, geçersiz kılma kontrol altına almalı mı değil mi? Bu tür birden çok geçersiz kılma varsa, önce kontrolü kim alır? ikinci? Örneğin nesne geçersiz kılmalarıyla, bu soruların hepsinin anlamlı ve iyi gerekçeli cevapları vardır, ancak statik ile yoktur.

Statik için geçersiz kılmalar büyük ölçüde kaotik olurdu ve bunun gibi şeyler daha önce yapılmıştı.

Örneğin, Mac OS System 7 ve önceki sürümlerde, işletim sisteminden önce uygulama tarafından yapılan sistem çağrılarının kontrolünü alarak sistemi genişletmek için bir tuzak yama mekanizması kullanılmıştır. Sistem çağrısı yama tablosunu, tek bir genel tablo olması dışında, örneğin nesneler için bir vtable gibi bir işlev işaretçileri dizisi olarak düşünebilirsiniz .

Bu, tuzak yamalarının düzensiz doğası nedeniyle programcılar için anlatılmamış kedere neden oldu. Kim tuzağa düşürmek isterse, temelde kazandı, istemeseler bile. Tuzağın her bir kullanıcısı, son derece kırılgan olan bir tür üst çağrı yeteneği için önceki tuzak değerini yakalayacaktır. Bir tuzak yamasını kaldırmak, bir sistem çağrısı hakkında artık bilgi sahibi olmanız gerekmediğinde, yamanızın gerçekten kaldırılması için gerekli bilgilere sahip olmadığınız için kötü bir form olarak kabul edildiğini söyleyin (eğer yaptıysanız, takip eden diğer yamaları da kaldıracaksınız) sen).

Bu, statiklerin geçersiz kılınması için bir mekanizma oluşturmanın imkansız olduğu anlamına gelmez, ancak bunun yerine muhtemelen tercih etmeyi tercih ettiğim, statik alanları ve statik yöntemleri örnek alanlarına ve metasınıfların örnek yöntemlerine dönüştürmektir, böylece normal nesne oryantasyon teknikleri uygulanacaktır. Bunu da yapan sistemlerin olduğuna dikkat edin: CSE 341: Smalltalk sınıfları ve metasınıfları ; Ayrıca bkz: Java statikinin Smalltalk eşdeğeri nedir?


Makul bir şekilde çalışmasını sağlamak için ciddi bir dil özelliği tasarımı yapmanız gerektiğini söylemeye çalışıyorum. Bir örnek olarak, saf bir yaklaşım yapılmış, topallamış, ancak çok sorunlu ve tartışmasız (yani tartışacağım) eksik ve kullanımı zor bir soyutlama sağlayarak mimari olarak kusurluydu.

Statik geçersiz kılma özelliğini iyi çalışacak şekilde tasarladığınızda, OOP'un sınıf tabanlı yöntemlere doğal bir uzantısı olan bir çeşit metasınıf icat etmiş olabilirsiniz. Yani, bunu yapmamak için hiçbir neden yok - ve bazı diller aslında yapıyor. Belki bir takım dillerin yapmamayı seçmesi biraz daha zorlayıcıdır.


Doğru anlıyorsam: teorik olarak böyle bir şey yapmak isteyip istemediğiniz meselesi değil, daha çok programsal olarak, uygulanması zor ve zorunlu olarak yararlı olmaz mı?
PixelArtDragon

@Garan, Zeyilnamemi görün.
Erik Eidt

1
Sipariş argümanını almıyorum; yöntemi çağırdığınız sınıf tarafından sağlanan yöntemi (veya dilinizin seçili doğrusallaştırmasına göre yöntem sağlayan ilk üst sınıf) alırsınız .
Ocaklar

1
@PierreArlaud: Ancak this.instanceMethod()dinamik çözünürlüğe sahiptir, bu nedenle self.staticMethod()aynı çözünürlüğe, yani dinamik değere sahip olsaydı , artık statik bir yöntem olmazdı.
Jörg W Mittag

1
statik yöntemler için geçersiz kılınan anlambilimin eklenmesi gerekir. Varsayımsal bir Java + metasınıfının hiçbir şey eklemesine gerek yoktur! Sadece sınıf nesneleri yaparsınız ve statik yöntemler daha sonra normal örnek yöntemler haline gelir. Dile bir şey eklemeniz gerekmez, çünkü yalnızca zaten var olan kavramları kullanırsınız: nesneler, sınıflar ve örnek yöntemleri. Bonus olarak, statik yöntemleri dilden kaldırmanız mümkün ! Sen edebiliyoruz eklemek tarafından bir özellik çıkarma dilinden bir kavram ve dolayısıyla karmaşıklığı!
Jörg W Mittag

9

Geçersiz kılma sanal gönderime bağlıdır: thishangi yöntemi çağıracağınıza karar vermek için parametrenin çalışma zamanı türünü kullanırsınız . Statik bir yöntemin thisparametresi yoktur , bu nedenle gönderilecek hiçbir şey yoktur.

Bazı diller, özellikle Delphi ve Python, aşağıdakileri sağlayan bir "arada" kapsamına sahiptir: sınıf yöntemleri. Bir sınıf yöntemi sıradan bir örnek yöntemi değildir, ancak statik de değildir; o türün bir örneğinden ziyade nesne türünün kendisine referans olan bir selfparametre (eğlenceli bir şekilde, her iki dil de thisparametre olarak adlandırılır self) alır. Bu değerle, artık sanal dağıtım yapmak için kullanabileceğiniz bir türünüz var.

Ne yazık ki, ne JVM ne de CLR'nin karşılaştırılabilir bir şeyi yoktur.


selfGeçersiz kılınabilen yöntemlerle gerçek, somut nesneler olarak sınıfları kullanan diller mi - Smalltalk, elbette Ruby, Dart, Objective C, Self, Python? Yansıma yansıması olsa bile, sınıfların birinci sınıf nesneler olmadığı C ++ 'dan sonra gelen dillerle karşılaştırıldığında?
Jerry101

Açıkça söylemek gerekirse, Python veya Delphi'de sınıf yöntemlerini geçersiz kılabileceğinizi mi söylüyorsunuz?
Erik Eidt

@ErikEidt Python'da hemen hemen her şey geçersiz kılınabilir. Delphi'de, bir sınıf yöntemi, virtualörneğin örnek yöntemleri gibi, açık bir sınıfta açıkça bildirilebilir ve sonra geçersiz kılınabilir.
Mason Wheeler

Yani, bu diller bir çeşit metasınıf kavramı sağlıyor, değil mi?
Erik Eidt

1
@ErikEidt Python'un "gerçek" metasınıfları vardır. Delphi'de, sınıf başvurusu kendi içinde bir sınıf değil, özel bir veri türüdür.
Mason Wheeler

3

Sen sor

Statik fonksiyonlar neden geçersiz kılınmamalıdır?

soruyorum

Geçersiz kılmak istediğiniz işlevler neden statik olmalıdır?

Bazı diller gösteriyi statik bir yöntemle başlatmaya zorlar. Ancak bundan sonra daha fazla statik yöntem olmadan çok fazla sorunu çözebilirsiniz.

Bazı insanlar, bir nesnede duruma bağımlı olmadığında statik yöntemleri kullanmayı severler. Bazı insanlar diğer nesnelerin inşası için statik yöntemler kullanmayı sever. Bazı insanlar statik yöntemlerden mümkün olduğunca kaçınmayı sever.

Bu insanların hiçbiri yanlış değil.

Geçersiz kılmanız gerekirse, statik olarak etiketlemeyi bırakın. Hiçbir şey kırılamayacak çünkü etrafta vatansız bir cisim var.


Dil yapıları destekliyorsa, birim türü, mantıksal giriş yöntemine iletilen sıfır boyutlu bir yapı olabilir. Bu nesnenin oluşturulması bir uygulama detayıdır. Ve gerçek gerçek olarak uygulama ayrıntıları hakkında konuşmaya başlarsak, gerçek dünyadaki bir uygulamada, sıfır boyutlu bir türün gerçekten başlatılması gerekmediğini de düşünmeniz gerekir (çünkü yüklemek için herhangi bir talimat gerekmez. veya saklayın).
Theodoros Chatzigiannakis

Ve daha da fazla "perde arkasında" (örneğin yürütülebilir düzeyde) konuşuyorsanız, ortam argümanları dilde bir tür olarak tanımlanabilir ve programın "gerçek" giriş noktası bir örnek yöntemi olabilir. işletim sistemi yükleyicisi tarafından programa aktarılan her örnek üzerinde etkili olan bu tür.
Theodoros Chatzigiannakis

Bence ona nasıl baktığına bağlı. Örneğin, bir program başlatıldığında, işletim sistemi programı belleğe yükler ve daha sonra programın ikili giriş noktasını tek tek uygulamasının bulunduğu adrese gider (örn _start(). Linux üzerindeki sembol). Bu örnekte, yürütülebilir nesne (kendi "dağıtım tablosu" ve her şeye sahiptir) ve giriş noktası dinamik olarak gönderilen işlemdir. Bu nedenle, bir programın giriş noktası dışarıdan bakıldığında doğal olarak sanaldır ve içeriden de bakıldığında sanal görünmesi kolayca yapılabilir.
Theodoros Chatzigiannakis

1
"Hiçbir şey kırılamayacak çünkü etrafta vatansız bir cisim var." ++++++
RubberDuck

@TheodorosChatzigiannakis güçlü bir argüman yaparsınız. eğer beni başka bir şey ikna etmediysen, çizgi istediğim düşünce çizgisine ilham vermiyor. İnsanlara, körü körüne statik yöntemlerle ilişkilendirdikleri daha alışılmış uygulamalardan bazılarını silkme izni vermeye çalışıyordum. Bu yüzden güncelledim. Düşünceler?
candied_orange

2

Statik yöntemler neden geçersiz kılınmamalıdır?

Bu bir "zorunluluk" meselesi değil.

"Geçersiz kılma", " dinamik olarak gönderme" anlamına gelir . "Statik yöntem", "statik olarak gönderme" anlamına gelir. Bir şey statikse, geçersiz kılınamaz. Bir şey geçersiz kılınabilirse, statik değildir.

Sorunuz şu soruyu sormak istiyor: "Üç tekerlekli bisikletler neden dört tekerleğe sahip olamıyor?" Tanımı "üç tekerlekli bisiklet" o üç tekerleği var olduğunu. Bir üç tekerlekli bisikletse, dört tekerleği olamaz, dört tekerleği varsa, bir üç tekerlekli bisiklet olamaz. Benzer şekilde, "statik yöntem" tanımı statik olarak gönderilmesidir. Statik bir yöntemse, dinamik olarak gönderilemez, dinamik olarak gönderilebilirse, statik bir yöntem olamaz.

Elbette, geçersiz kılınabilecek sınıf yöntemlerine sahip olmak mükemmel bir şekilde mümkündür . Veya sınıf gibi diğer nesneler gibi nesneler olduğu ve böylece sınıf yöntemlerine olan ihtiyacı tamamen ortadan kaldıran örnek yöntemlere sahip olabileceği Ruby gibi bir dile sahip olabilirsiniz. (Ruby'nin yalnızca bir tür yöntemi vardır: örnek yöntemleri. Sınıf yöntemleri, statik yöntemler, yapıcılar, işlevler veya yordamlar yoktur.)


1
"Tanımı gereği"
candied_orange

1

bu nedenle C # 'daki statik fonksiyonlar sanal veya soyut olamaz

C # 'da, her zaman sınıfı kullanarak statik üyeleri çağırırsınız, örneğin BaseClass.StaticMethod(), değil baseObject.StaticMethod(). Sonuç olarak, ChildClassdevralınan BaseClassve childObjectbir örneğiniz ChildClassvarsa, statik yönteminizi şundan çağıramazsınız childObject. Her zaman açıkça gerçek sınıfı kullanmanız gerekecek, bu yüzden static virtualmantıklı değil.

Yapabileceğiniz şey, staticalt sınıfınızda aynı yöntemi yeniden tanımlamak ve newanahtar kelimeyi kullanmaktır .

class BaseClass {
    public static int StaticMethod() { return 1; }
}

class ChildClass {
    public static new int StaticMethod() { return BaseClass.StaticMethod() + 2; }
}

int result;    
var baseObj = new BaseClass();
var childObj = new ChildClass();

result = BaseClass.StaticMethod();
result = baseObj.StaticMethod(); // DOES NOT COMPILE

result = ChildClass.StaticMethod();
result = childObj.StaticMethod(); // DOES NOT COMPILE

Aramak mümkün baseObject.StaticMethod()olsaydı, sorunuz mantıklı olurdu.

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.