Sanal olmayan bir yöntemi geçersiz kılmak mümkün mü?


92

Sanal olmayan bir yöntemi geçersiz kılmanın herhangi bir yolu var mı? veya benzer sonuçlar veren bir şey (istenen yöntemi çağırmak için yeni bir yöntem oluşturmak dışında)?

Microsoft.Xna.Framework.Graphics.GraphicsDeviceBirim testini göz önünde bulundurarak bir yöntemi geçersiz kılmak istiyorum .


8
Aşırı yükleme mi yoksa geçersiz kılmayı mı kastediyorsunuz ? Overload = aynı ada ancak farklı parametrelere sahip bir yöntem ekleyin (örn. Console.WriteLine'ın farklı aşırı yüklemeleri). Override = (kabaca) yöntemin varsayılan davranışını değiştirir (örneğin, Circle, Rectangle, vb. İçin farklı davranışa sahip bir Shape.Draw yöntemi). Türetilmiş bir sınıftaki bir yöntemi her zaman aşırı yükleyebilirsiniz , ancak geçersiz kılma yalnızca sanal yöntemler için geçerlidir.
itowlson

Yanıtlar:


112

Hayır, sanal olmayan bir yöntemi geçersiz kılamazsınız. Yapabileceğiniz en yakın şey new, aynı isimde bir yöntem oluşturarak yöntemi gizlemektir, ancak bu, iyi tasarım ilkelerini ihlal ettiği için tavsiye edilmez.

Ancak bir yöntemi gizlemek bile size gerçek bir sanal yöntem çağrısının yapacağı gibi yöntem çağrılarının yürütme zamanı polimorfik gönderimini vermez. Şu örneği düşünün:

using System;

class Example
{
    static void Main()
    {
        Foo f = new Foo();
        f.M();

        Foo b = new Bar();
        b.M();
    }
}

class Foo
{
    public void M()
    {
        Console.WriteLine("Foo.M");
    }
}

class Bar : Foo
{
    public new void M()
    {
        Console.WriteLine("Bar.M");
    }
}

Bu örnekte her iki Myöntem de print yöntemini çağırmaktadır Foo.M. Bu yaklaşımı görebileceğiniz gibi bu nesneye referans doğru türetilmiş bir tür olmasına karşın taban yöntemi gizleme olduğu sürece bir yöntem için yeni bir uygulama kullanmasına olanak tanır yapar mola polimorfizmi.

Temel yöntemleri bu şekilde gizlememenizi tavsiye ederim.

Yöntemlerin varsayılan olarak sanal olmadığı (Java'nın aksine) C # 'ın varsayılan davranışını destekleyenlerin yanında olma eğilimindeyim. Daha da ileri gidip sınıfların da varsayılan olarak mühürlenmesi gerektiğini söylerdim. Kalıtımın doğru bir şekilde tasarlanması zordur ve sanal olarak işaretlenmemiş bir yöntemin varlığı, bu yöntemin yazarının yöntemin geçersiz kılınmasını asla amaçlamadığını gösterir.

Düzenleme: "yürütme zamanı polimorfik gönderme" :

Bununla kastettiğim, sanal yöntemleri çağırdığınızda yürütme sırasında meydana gelen varsayılan davranıştır. Diyelim ki önceki kod örneğimde sanal olmayan bir yöntemi tanımlamak yerine, aslında bir sanal yöntem ve gerçek bir geçersiz kılınan yöntem tanımladım.

Bu b.Foodurumda arayacak olsaydım , CLR breferansın işaret ettiği nesne türünü doğru bir şekilde belirler Barve çağrıyı Muygun şekilde gönderir .


6
"Yürütme zamanı polimorfik gönderme" teknik olarak bunu söylemenin doğru yolu olsa da, bunun muhtemelen hemen herkesin kafasını aştığını tahmin ediyorum!
Orion Edwards

3
Yazarın yöntemin geçersiz kılınmasına izin vermeme niyetinde olduğu doğru olsa da, bunun mutlaka yapılması gereken doğru şey olduğu doğru değildir. XNA ekibinin, kullanıcıya hata ayıklama ve birim testinde daha fazla esneklik sağlamak için bir IGraphicsDevice arayüzü uygulaması gerektiğini düşünüyorum. Bazı çok çirkin şeyler yapmak zorunda kaldım ve bu ekip tarafından önceden görülmeliydi. Daha fazla tartışma burada bulunabilir: forums.xna.com/forums/p/30645/226222.aspx
zfedoran

2
@Orion Ben de anlamadım ama kısa bir google'dan sonra "doğru" terimlerin kullanıldığını görünce takdir ettim.
zfedoran

10
"Yazar buna niyet etmedi" argümanına hiç inanmıyorum. Bu, tekerleğin mucidinin tekerleklerin arabalara takılacağını tahmin etmediğini, bu yüzden onları arabalarda kullanamayacağımızı söylemek gibi. Tekerleğin / yöntemin nasıl çalıştığını biliyorum ve onunla biraz farklı bir şey yapmak istiyorum. Yaratıcının beni isteyip istememesi umurumda değil. Ölü bir ipliği canlandırmaya çalıştığım için özür dilerim. İçinde bulunduğum bu sorunu aşmanın gerçekten uygunsuz bir yolunu bulmadan önce biraz buhar atmam gerekiyor :)
Jeff

2
Peki ya büyük bir kod tabanını devraldığınızda ve yeni işlevler için bir test eklemeniz gerektiğinde, ancak test etmek için çok sayıda makineyi başlatmanız gerektiğinde ne olacak? Java'da, yalnızca bu parçaları genişletir ve saplamalar için gereken yöntemleri geçersiz kılardım. C # 'da, yöntemler sanal olarak işaretlenmemişse, başka bir mekanizma bulmam gerekir (alaycı kitaplık veya benzeri ve hatta o zaman bile).
Adam Parkin

22

Hayır yapamazsın.

Yalnızca sanal bir yöntemi geçersiz kılabilirsiniz - buradan MSDN'ye bakın :

C # 'da türetilmiş sınıflar, temel sınıf yöntemleriyle aynı ada sahip yöntemler içerebilir.

  • Temel sınıf yöntemi sanal olarak tanımlanmalıdır.

6

Temel sınıf mühürlenmemişse, ondan miras alabilir ve temel olanı gizleyen yeni bir yöntem yazabilirsiniz (yöntem bildiriminde "new" anahtar sözcüğünü kullanın). Aksi takdirde hayır, onu geçersiz kılamazsınız çünkü orijinal yazarlar asla geçersiz kılınmak istemedi, bu yüzden sanal değildir.


3

Bence aşırı yükleme yapıyorsunuz ve kafanız karışıyor, aşırı yükleme, aynı ada sahip iki veya daha fazla yönteme sahip olduğunuz, ancak farklı parametre kümelerine sahip olduğunuz anlamına gelir; geçersiz kılma, türetilmiş bir sınıfta bir yöntem için farklı bir uygulamaya sahip olduğunuz anlamına gelir (böylece davranışı değiştirir veya değiştirirsiniz) temel sınıfta).

Bir yöntem sanalsa, derrived sınıftaki override anahtar sözcüğünü kullanarak onu geçersiz kılabilirsiniz. Bununla birlikte, sanal olmayan yöntemler yalnızca override anahtar sözcüğü yerine new anahtar sözcüğünü kullanarak temel uygulamayı gizleyebilir. Sanal olmayan yol, arayan kişi yönteme temel tür olarak yazılan bir değişken aracılığıyla erişirse, derleyici temel yönteme statik bir gönderim kullanacağından işe yaramaz (yani, derrived sınıfınızdaki kod asla çağrılmaz).

Var olan bir sınıfa aşırı yükleme eklemenizi engelleyen hiçbir şey yoktur, ancak yalnızca sınıfınız hakkında bilgi sahibi olan kod ona erişebilir.


2

C # 'daki herhangi bir sınıfın sanal olmayan yöntemini geçersiz kılamazsınız (CLR'yi kırmadan), ancak sınıfın uyguladığı herhangi bir arabirim yöntemini geçersiz kılabilirsiniz. Mühürsüz olduğumuzu düşünün

class GraphicsDevice: IGraphicsDevice {
    public void DoWork() {
        Console.WriteLine("GraphicsDevice.DoWork()");
    }
}

// with its interface
interface IGraphicsDevice {
    void DoWork();
}

// You can't just override DoWork in a child class,
// but if you replace usage of GraphicsDevice to IGraphicsDevice,
// then you can override this method (and, actually, the whole interface).

class MyDevice: GraphicsDevice, IGraphicsDevice {
    public new void DoWork() {
        Console.WriteLine("MyDevice.DoWork()");
        base.DoWork();
    }
}

Ve işte demo

class Program {
    static void Main(string[] args) {

        IGraphicsDevice real = new GraphicsDevice();
        var myObj = new MyDevice();

        // demo that interface override works
        GraphicsDevice myCastedToBase = myObj;
        IGraphicsDevice my = myCastedToBase;

        // obvious
        Console.WriteLine("Using real GraphicsDevice:");
        real.DoWork();

        // override
        Console.WriteLine("Using overriden GraphicsDevice:");
        my.DoWork();

    }
}


0

Türetilmemiş bir sınıftan miras alıyorsanız, basitçe soyut bir süper sınıf oluşturabilir ve bunun yerine aşağı akıştan miras alabilirsiniz.


0

Sanal olmayan bir yöntemi geçersiz kılmanın herhangi bir yolu var mı? veya benzer sonuçlar veren bir şey (istenen yöntemi çağırmak için yeni bir yöntem oluşturmak dışında)?

Sanal olmayan bir yöntemi geçersiz kılamazsınız. Ancak yapabilirsiniz kullanmak newbenzer sonuçlar elde etmek için düzenleyici anahtar kelime:

class Class0
{
    public int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    public new int Test()
    {
        return 1;
    }
}
. . .
// result of 1
Console.WriteLine(new Class1().Test());

Ayrıca erişim değiştiricinin de aynı olduğundan emin olmak isteyeceksiniz , aksi takdirde altta miras almayacaksınız. Eğer başka bir sınıf Class1in newanahtar sözcüğünden miras alırsa Class1, erişim değiştiricisi aynı olmadığı sürece, ondan devralan nesneleri etkilemez.

Erişim değiştirici aynı değilse :

class Class0
{
    protected int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    // different access modifier
    new int Test()
    {
        return 1;
    }
}

class Class2 : Class1
{
    public int Result()
    {
        return Test();
    }
}
. . .
// result of 0
Console.WriteLine(new Class2().Result());

... erişim değiştirici eğer karşı olduğunu aynı:

class Class0
{
    protected int Test()
    {
        return 0;
    }
}

class Class1 : Class0
{
    // same access modifier
    protected new int Test()
    {
        return 1;
    }
}

class Class2 : Class1
{
    public int Result()
    {
        return Test();
    }
}
. . .
// result of 1
Console.WriteLine(new Class2().Result());

Daha önceki bir cevapta belirtildiği gibi, bu iyi bir tasarım ilkesi değildir.


-5

Soyut sınıf ve soyut yöntem kullanarak bunu başarmanın bir yolu var.

Düşünmek

Class Base
{
     void MethodToBeTested()
     {
        ...
     }

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

Şimdi, MethodToBeTested () yönteminin farklı sürümlerine sahip olmak istiyorsanız, Class Base'i soyut bir sınıfa ve MethodToBeTested () yöntemini soyut bir yöntem olarak değiştirin.

abstract Class Base
{

     abstract void MethodToBeTested();

     void Method1()
     {
     }

     void Method2()
     {
     }

     ...
}

Soyut void ile MethodToBeTested () bir sorun ortaya çıkar; uygulama gitti.

Bu nedenle class DefaultBaseImplementation : Base, varsayılan uygulamaya sahip olmak için bir oluşturun .

Ve class UnitTestImplementation : Basebirim testi uygulamasına sahip olmak için başka bir tane oluşturun .

Bu 2 yeni sınıfla, temel sınıf işlevselliği geçersiz kılınabilir.

Class DefaultBaseImplementation : Base    
{
    override void MethodToBeTested()    
    {    
        //Base (default) implementation goes here    
    }

}

Class UnitTestImplementation : Base
{

    override void MethodToBeTested()    
    {    
        //Unit test implementation goes here    
    }

}

Şimdi uygulayan (geçersiz kılan) 2 sınıfınız var MethodToBeTested().

(Türetilmiş) sınıfı gerektiği gibi başlatabilirsiniz (yani temel uygulama ile veya birim testi uygulamasıyla).


@slavoo: Merhaba slavoo. Kod güncellemesi için teşekkürler. Ama lütfen bana bunu olumsuz oylamanın nedenini söyler misin?
ShivanandSK

6
Çünkü soruyu cevaplamıyor. Sanal olarak işaretlenmemiş üyeleri geçersiz kılıp kılamayacağınızı soruyor. Özet olarak işaretlenmiş üyeleri uygulamanız gerektiğini kanıtladınız.
Lee Louviere
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.