Yeni ve geçersiz kılma arasındaki fark


200

Aşağıdakiler arasındaki farkın ne olduğunu merak ediyorum:

Durum 1: Temel Sınıf

public void DoIt();

Durum 1: Devralınan sınıf

public new void DoIt();

Durum 2: Temel Sınıf

public virtual void DoIt();

Durum 2: Miras alınan sınıf

public override void DoIt();

Her iki durum 1 ve 2, yaptığım testlere göre aynı etkiye sahip gibi görünüyor. Bir fark veya tercih edilen bir yol var mı?


Yanıtlar:


269

Geçersiz kılma değiştiricisi sanal yöntemlerde kullanılabilir ve soyut yöntemlerde kullanılmalıdır. Bu, derleyicinin bir yöntemin en son tanımlanmış uygulamasını kullanmasını gösterir. Yöntem, temel sınıfa başvuru üzerine çağrılsa bile, onu geçersiz kılan uygulamayı kullanacaktır.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public override void DoIt()
    {
    }
}

Base b = new Derived();
b.DoIt();                      // Calls Derived.DoIt

Derived.DoItgeçersiz kılınırsa arayacaktır Base.DoIt.

Yeni değiştirici, derleyiciye üst sınıf uygulaması yerine alt sınıf uygulamanızı kullanmasını bildirir. Sınıfınıza başvuruda bulunmayan ancak üst sınıftaki tüm kodlar, üst sınıf uygulamasını kullanır.

public class Base
{
    public virtual void DoIt()
    {
    }
}

public class Derived : Base
{
    public new void DoIt()
    {
    }
}

Base b = new Derived();
Derived d = new Derived();

b.DoIt();                      // Calls Base.DoIt
d.DoIt();                      // Calls Derived.DoIt

Önce arayacak Base.DoItsonraDerived.DoIt . Bunlar, temel yöntemi geçersiz kılan türetilmiş yöntem yerine, aynı ada sahip iki tamamen ayrı yöntemdir.

Kaynak: Microsoft blogu


5
This indicates for the compiler to use the last defined implementation of a method. bir yöntemin son tanımlı uygulamasını nasıl bulabilirim?
AminM

5
Somut bir sınıftan başlayın, ilgilenilen yöntemin uygulanıp uygulanmadığını kontrol edin. Eğer öyleyse, işiniz bitti. Değilse, miras hiyerarşisinde bir adım yukarı gidin, yani, süper sınıfın ilgi yöntemine sahip olup olmadığını kontrol edin. İlgilenilen yöntemi bulana kadar devam edin.
csoltenborn

2
Ayrıca, yalnızca overridebase class yöntemi şu şekilde tanımladığında bir yöntem yapabileceğinizi unutmayın:virtual . Sözcük virtualben bu yöntemi çağırdığınızda aslında zamanında aramıştım hangi yöntemi uygulaması gerçekten önceden bilmiyorum Hey, neredeyse, bir türetilmiş uygulanmasıyla yerini almış" diyerek temel sınıftır. Yani virtualanlamına hangi bir . bir yönteme tutucu Bu olarak işaretlenmemiş olduğu yöntem eder virtualkılınmış olamaz Ama olabilir. yerini modifiye edici ile türetilmiş bir sınıf içinde olmayan herhangi bir sanal bir yöntem newtüretilmiş seviyede erişilebilir.
Erik Bongers

177

virtual : bir yöntemin mirasçı tarafından geçersiz kılınabileceğini gösterir

geçersiz kıl : temel işlevdeki sanal yöntemin işlevselliğini geçersiz kılar ve farklı işlevler sağlar.

yeni : Farklı işlevler sağlayan orijinal yöntemi (sanal olması gerekmez) gizler . Bu sadece kesinlikle gerekli olduğu yerlerde kullanılmalıdır.

Bir yöntemi gizlediğinizde, yine de temel sınıfa döküm yaparak orijinal yönteme erişebilirsiniz. Bu bazı senaryolarda kullanışlıdır, ancak tehlikelidir.


2
Neden taban yöntemini gizleyen bir yöntem dökmek tehlikelidir? Yoksa genel olarak dökümün tehlikeli olduğunu mu ima ediyorsunuz?
Mark

3
@ Mark - bir arayan uygulamanın farkında olmayabilir ve yanlışlıkla yanlış kullanıma neden olabilir.
Jon B

Ana yöntemde overrideve / veya newolmadan kullanabilir misiniz virtual?
Aaron Franke

16

İlk durumda üst sınıftaki tanımı gizlersiniz. Bu, yalnızca nesne ile alt sınıf olarak uğraşırken çağrılacağı anlamına gelir. Sınıfı üst türüne aktarırsanız, üst öğenin yöntemi çağrılır. İkinci durumda, yöntem geçersiz kılınır ve nesnenin alt sınıf veya üst sınıf olarak kullanılmasına bakılmaksızın çağrılır.


7

aşağıdakileri deneyin: (case1)

((BaseClass)(new InheritedClass())).DoIt()

Düzenleme: sanal + geçersiz kılma çalışma zamanında çözülür (böylece geçersiz kılma sanal yöntemleri geçersiz kılar), yeni sadece aynı ada sahip yeni bir yöntem oluşturur ve eskiyi gizler, derleme zamanında çözülür -> derleyiciniz yöntemi çağırır ' görür


3

1 kullandıysanız, tür temel sınıf olarak bildirilirken devralınan sınıfın DoIt () yöntemini çağırırsanız, temel sınıfın eylemini bile görürsünüz.

/* Results
Class1
Base1
Class2
Class2
*/
public abstract class Base1
{
    public void DoIt() { Console.WriteLine("Base1"); }
}
public  class Class1 : Base1 
{
    public new void DoIt() { Console.WriteLine("Class1"); }
}
public abstract class Base2
{
    public virtual void DoIt() { Console.WriteLine("Base2"); }
}
public class Class2 : Base2
{
    public override void DoIt() { Console.WriteLine("Class2"); }
}
static void Main(string[] args)
{
    var c1 = new Class1();
    c1.DoIt();
    ((Base1)c1).DoIt();

    var c2 = new Class2();
    c2.DoIt();
    ((Base2)c2).DoIt();
    Console.Read();
}

Aldığınız uyarı veya hatayı gönderebilir misiniz? Bu kod, başlangıçta gönderdiğimde iyi çalıştı.
Matthew Whites

Bunların tümü giriş noktası sınıfınıza (Program) yapıştırılmalıdır. Bu, bu sitede daha iyi biçimlendirmeye izin vermek için kaldırıldı.
Matthew

3

İki durum arasındaki fark, durum 1'de temel DoItyöntemin geçersiz kılınmaması, sadece gizlenmesidir. Bunun anlamı, değişkenin türüne bağlı olarak hangi yöntemin çağrılacağına bağlı olmasıdır. Örneğin:

BaseClass instance1 = new SubClass();
instance1.DoIt(); // Calls base class DoIt method

SubClass instance2 = new SubClass();
instance2.DoIt(); // Calls sub class DoIt method

Bu gerçekten kafa karıştırıcı olabilir ve beklenmeyen davranışlarla sonuçlanır ve mümkünse kaçınılmalıdır. Bu yüzden tercih edilen yol durum 2'dir.


3
  • newREFERANS türünüze (sol taraf =) saygı göstererek, başvuru türlerinin yöntemini çalıştırmayı ifade eder. Yeniden tanımlı yöntemin newanahtar sözcüğü yoksa , olduğu gibi davranır. Ayrıca, polimorfik olmayan kalıtım olarak da bilinir . Yani, “Türetilmiş sınıfta, taban sınıfta aynı ada sahip herhangi bir yöntemle kesinlikle ilgisi olmayan yepyeni bir yöntem yapıyorum.” - dedi Whitaker
  • override, virtualtemel sınıfında anahtar kelime ile kullanılması gereken , OBJECT türünüze (sağ tarafı =) saygı gösterilmesi anlamına gelir , böylece başvuru türünden bağımsız olarak çalıştırma yöntemi geçersiz kılınır. Ayrıca, polimorfik kalıtım olarak da bilinir .

Aklımda iki anahtar kelime birbirlerinin zıt olduğunu unutmayın.

override: virtualyöntemi geçersiz kılmak için anahtar kelime tanımlanmalıdır. Yöntemi kullanarakoverrideTemel sınıfla başlatılırsa, başvuru türünden (temel sınıf veya türetilmiş sınıf başvurusu) bağımsız olarak anahtar sözcük yöntem, temel sınıf yöntemi çalışır. Aksi takdirde, türetilmiş sınıf yöntemi çalışır.

new: anahtar kelime bir yöntem tarafından kullanılıyorsa, anahtar kelimenin aksine override, başvuru türü önemlidir. Türetilmiş sınıfla başlatılırsa ve başvuru türü temel sınıfsa, temel sınıf yöntemi çalışır. Türetilmiş sınıfla başlatılırsa ve başvuru türü türetilmiş sınıfsa, türetilmiş sınıf yöntemi çalışır. Yani, overrideanahtar kelimenin kontrastıdır . Geçtiğinde, yönteme yeni anahtar kelime eklemeyi unutursanız veya atlarsanız, derleyici varsayılan olarak newanahtar sözcük kullanıldıkça davranır .

class A 
{
    public string Foo() 
    {
        return "A";
    }

    public virtual string Test()
    {
        return "base test";
    }
}

class B: A
{
    public new string Foo() 
    {
        return "B";
    }
}

class C: B 
{
    public string Foo() 
    {
        return "C";
    }

    public override string Test() {
        return "derived test";
    }
}

Ana çağrı:

A AClass = new B();
Console.WriteLine(AClass.Foo());
B BClass = new B();
Console.WriteLine(BClass.Foo());
B BClassWithC = new C();
Console.WriteLine(BClassWithC.Foo());

Console.WriteLine(AClass.Test());
Console.WriteLine(BClassWithC.Test());

Çıktı:

A
B
B
base test
derived test

Yeni kod örneği,

Kodları tek tek yorumlayarak oynayabilirsiniz.

class X
{
    protected internal /*virtual*/ void Method()
    {
        WriteLine("X");
    }
}
class Y : X
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Y");
    }
}
class Z : Y
{
    protected internal /*override*/ void Method()
    {
        base.Method();
        WriteLine("Z");
    }
}

class Programxyz
{
    private static void Main(string[] args)
    {
        X v = new Z();
        //Y v = new Z();
        //Z v = new Z();
        v.Method();
}

1

Anahtar kelime overridetüretme sınıfında kullanılıyorsa, üst yöntem geçersiz kılınır.

Anahtar kelime newtüretme sınıfında kullanılırsa, üst yöntem tarafından saklanan yöntemi türetir.


1

Ben aynı soru vardı ve gerçekten kafa karıştırıcı, geçersiz kılma ve yeni anahtar kelimeler sadece tür temel sınıf ve türetilmiş sınıf değeri nesneleri ile çalışan düşünmelisiniz . Bu durumda yalnızca geçersiz kılma ve yeni etkisini göreceksiniz: Eğer varsa Yani eğer class Ave B, Bgelen devralır A, o zaman böyle bir nesnenin örneğini:

A a = new B();

Şimdi çağrı yöntemleri durumunu dikkate alacaktır. Geçersiz kıl : yöntemin işlevini genişlettiği anlamına gelir, daha sonra türetilmiş sınıftaki yöntemi kullanır, yeni ise derleyiciye türetilmiş sınıftaki yöntemi gizlemesini ve bunun yerine temel sınıftaki yöntemi kullanmasını söyler. İşte bu konuyla ilgili çok iyi bir görüş:

https://msdn.microsoft.com/EN-US/library/ms173153%28v=VS.140,d=hv.2%29.aspx?f=255&MSPPError=-2147217396


1

Aşağıdaki makale vb.net'te ancak yeni ve geçersiz kılmalar hakkındaki açıklamanın anlaşılması çok kolay olduğunu düşünüyorum.

https://www.codeproject.com/articles/17477/the-dark-shadow-of-overrides

Makalenin bir noktasında şu cümle var:

Genel olarak, Gölgeler türle ilişkili işlevin çağrıldığını varsayarken Geçersiz Kılmalar nesne uygulamasının yürütüldüğünü varsayar.

Bu soruya kabul edilen cevap mükemmel, ancak bu makalenin bu iki anahtar kelime arasındaki farklar hakkında daha iyi anlam eklemek için iyi örnekler sağladığını düşünüyorum.


1

Bunların dışında, en kafa karıştırıcı yeni . Deneme yoluyla, yeni anahtar kelime, geliştiricilere türü açık bir şekilde tanımlayarak devralma sınıfı uygulamasını temel sınıf uygulamasıyla geçersiz kılma seçeneği sunar. Bu tam tersini düşünmek gibidir.

Aşağıdaki örnekte, tür açıkça BaseClass testi olarak tanımlanana kadar sonuç "Türetilmiş sonuç" döndürür, ancak "Temel sonuç" döndürülür.

class Program
{
    static void Main(string[] args)
    {
        var test = new DerivedClass();
        var result = test.DoSomething();
    }
}

class BaseClass
{
    public virtual string DoSomething()
    {
        return "Base result";
    }
}

class DerivedClass : BaseClass
{
    public new string DoSomething()
    {
        return "Derived result";
    }
}

3
İtiraz ederseniz yorumunuzu ekleyin. Vur ve kaç çok korkak.
usefulBee

0

Bu testlerde fonksiyonel fark gösterilmeyecektir:

BaseClass bc = new BaseClass();

bc.DoIt();

DerivedClass dc = new DerivedClass();

dc.ShowIt();

Bu örnekte, çağrılan Doit çağrılmasını beklediğiniz karakterdir.

Farkı görmek için bunu yapmanız gerekir:

BaseClass obj = new DerivedClass();

obj.DoIt();

Eğer durum 1 (bunu tanımlandığı gibi) içinde, o testi eğer göreceksiniz DoIt()içinde BaseClassdenir (bunu tanımlandığı gibi) halinde 2'de, DoIt()içinde DerivedClassadı verilir.


-1

İlk durumda, yeni anahtar kelime temel sınıf DoIt () yöntemini gizlediğinden türetilmiş sınıf DoIt () yöntemini çağırır.

İkinci durumda geçersiz kılınan DoIt ()

  public class A
{
    public virtual void DoIt()
    {
        Console.WriteLine("A::DoIt()");
    }
}

public class B : A
{
    new public void DoIt()
    {
        Console.WriteLine("B::DoIt()");
    }
}

public class C : A
{
    public override void DoIt()
    {
        Console.WriteLine("C::DoIt()");
    }
}

bu sınıfların bir örneğini oluşturalım

   A instanceA = new A();

    B instanceB = new B();
    C instanceC = new C();

    instanceA.DoIt(); //A::DoIt()
    instanceB.DoIt(); //B::DoIt()
    instanceC.DoIt(); //B::DoIt()

Her şey yukarıda bekleniyor. İnstanceB ve instanceC'yi instanceA olarak ayarlayın ve DoIt () yöntemini çağırın ve sonucu kontrol edin.

    instanceA = instanceB;
    instanceA.DoIt(); //A::DoIt() calls DoIt method in class A

    instanceA = instanceC;
    instanceA.DoIt();//C::DoIt() calls DoIt method in class C because it was overriden in class C

instanceC.DoIt (); B :: DoIt () değil, C :: DoIt () verecek
BYS2

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.