ความแตกต่างระหว่าง Shadowing และ Overriding ใน C #?


102

ความแตกต่างระหว่างการทำเงาและการแทนที่เมธอดใน C # คืออะไร?

คำตอบ:


154

มรดกดี ...

สมมติว่าคุณมีคลาสนี้:

class A {
   public int Foo(){ return 5;}
   public virtual int Bar(){return 5;}
}
class B : A{
   public new int Foo() { return 1;}     //shadow
   public override int Bar() {return 1;} //override
}

จากนั้นเมื่อคุณเรียกสิ่งนี้:

A clA = new A();
B clB = new B();

Console.WriteLine(clA.Foo()); // output 5
Console.WriteLine(clA.Bar()); // output 5
Console.WriteLine(clB.Foo()); // output 1
Console.WriteLine(clB.Bar()); // output 1

//now let's cast B to an A class
Console.WriteLine(((A)clB).Foo()); // output 5 <<<-- shadow
Console.WriteLine(((A)clB).Bar()); // output 1

สมมติว่าคุณมีคลาสพื้นฐานและคุณใช้คลาสพื้นฐานในโค้ดทั้งหมดของคุณแทนที่จะเป็นคลาสที่สืบทอดมาและคุณใช้เงามันจะส่งคืนค่าที่คลาสฐานส่งกลับแทนที่จะทำตามโครงสร้างการสืบทอดของประเภทจริงของอ็อบเจ็กต์

เรียกใช้รหัสที่นี่

หวังว่าฉันจะเข้าใจ :)


17
การลบล้างให้ความหลากหลายการสร้างเงาให้การใช้งานที่แตกต่างกันในระดับของลำดับชั้นนั้น ๆ แต่ไม่ใช่ความหลากหลาย
David Rodríguez - น้ำลายไหล

@dribeas คุณจะใช้คำจำกัดความของความหลากหลายแบบใดเพื่อสรุปข้อสรุปนั้น
AnthonyWJones

9
@AnthonyWJones Polymorphism คือความสามารถของชนิดหนึ่ง (ที่ได้มา) ที่จะใช้เป็นประเภท (ฐาน) ที่แตกต่างกันโดยที่การนำไปใช้จริง (พฤติกรรม) เป็นประเภทที่เฉพาะเจาะจงจริง (ที่ได้มา) หากคุณลบล้างวิธีการที่ได้รับจะถูกเรียกผ่านการอ้างอิงถึงฐาน แต่ถ้าคุณมีเงาที่ไม่เป็นจริง
David Rodríguez - dribeas

2
สำหรับฉันดูเหมือนว่าคุณมีข้อผิดพลาดในโค้ดตัวอย่างของคุณ ความหล่อควรเป็น ((A) clB) ฟู () ไม่ควรหรือ?
เทรนด์

1
ไม่เพราะบาร์เป็นแบบเสมือนดังนั้นจึงยังคงเรียก B Bar
Stormenet

32

Shadowing เป็นคำพูดของ VB สำหรับสิ่งที่เราอ้างถึงว่าซ่อนอยู่ใน C #

มักจะซ่อนตัวอยู่ (แชโดว์ใน VB) และที่สำคัญมีการแสดงให้เห็นว่าในคำตอบโดยStormenet

วิธีการเสมือนถูกแสดงให้เห็นว่าถูกแทนที่โดยคลาสย่อยและการเรียกใช้เมธอดนั้นแม้ในประเภทซุปเปอร์คลาสหรือจากโค้ดภายในของซูเปอร์คลาสจะเรียกการใช้งานทดแทนจากคลาสย่อย

จากนั้นวิธีการที่เป็นรูปธรรมจะปรากฏขึ้น (วิธีที่ไม่ถูกทำเครื่องหมายว่าเสมือนหรือนามธรรม) ถูกซ่อนโดยใช้newคำสำคัญเมื่อกำหนดวิธีการที่มีลายเซ็นที่เหมือนกันบนคลาสย่อย ในกรณีนี้เมื่อเมธอดถูกเรียกใช้ในประเภทซุปเปอร์คลาสที่ใช้การใช้งานดั้งเดิมการใช้งานใหม่จะพร้อมใช้งานบนคลาสย่อยเท่านั้น

อย่างไรก็ตามสิ่งที่มักจะพลาดคือสามารถซ่อนวิธีเสมือนได้

class A
{
    public virtual void DoStuff() { // original implementation }
}

class B : A
{
    public new void DoStuff() {  //new implementation }
}

B b = new B();
A a = b;

b.DoStuff(); //calls new implementation
a.DoStuff(); //calls original implementation.

หมายเหตุในตัวอย่างข้างต้น DoStuff กลายเป็นรูปธรรมและไม่สามารถลบล้างได้ อย่างไรก็ตามยังสามารถใช้ทั้งคำหลักvirtualและnewคำหลักร่วมกันได้

class A
{
    public virtual void DoStuff() { // original implementation }
}

class B : A
{
    public new virtual void DoStuff() {  //new implementation }
}

class C : B
{
    public override void DoStuff() { //replacement implementation }
}

C c = new C();
B b = c;
A a = b;

c.DoStuff(); //calls replacement implementation
b.DoStuff(); //calls replacement implementation
a.DoStuff(); //calls original implementation.

โปรดทราบว่าแม้วิธีการทั้งหมดที่เกี่ยวข้องจะเป็นเสมือนการแทนที่บน C จะไม่มีผลต่อวิธีการเสมือนบน A เนื่องจากการใช้newใน B จะซ่อนการใช้งาน A

แก้ไข:มีการระบุไว้ในความคิดเห็นของคำตอบนี้ว่าข้างต้นอาจเป็นอันตรายหรืออย่างน้อยก็ไม่มีประโยชน์อย่างยิ่ง ฉันจะบอกว่าใช่มันอาจเป็นอันตรายและจะอยู่ที่นั่นถ้ามันมีประโยชน์

โดยเฉพาะอย่างยิ่งคุณอาจประสบปัญหาทุกประเภทหากคุณเปลี่ยนตัวปรับแต่งความสามารถในการเข้าถึงด้วย ตัวอย่างเช่น:-

public class Foo
{
    internal Foo() { }
    protected virtual string Thing() { return "foo"; }
}

public class Bar : Foo
{
 internal new string Thing() { return "bar"; }
}

ไปยังทายาทภายนอกของBar, Fooของการดำเนินงานของสิ่งที่ () ยังคง accesible และ overridable ทั้งหมดถูกกฎหมายและสามารถอธิบายได้ตามกฎประเภท. NET ไม่เคยไม่ได้ตั้งใจเลยทีเดียว

ฉันได้โพสต์คำตอบนี้เพื่อทำความเข้าใจให้ลึกซึ้งยิ่งขึ้นว่าสิ่งต่างๆทำงานอย่างไรไม่ใช่คำแนะนำของเทคนิคที่สามารถใช้ได้อย่างอิสระ


2
ยินดีที่ได้รู้จัก แม้ว่าการใช้งานอาจเป็นอันตรายได้ :)
Stormenet

2
เครื่องมือทั้งหมดอาจเป็นอันตรายได้เมื่อใช้ไม่ถูกต้อง
AnthonyWJones

1
แต่การใช้งานมันดูน่าสงสัยจริงๆ! มีแอปโอเพนซอร์สที่ 'ร้ายแรง' ที่ใช้สิ่งนี้หรือไม่?
Jox

4
การใช้งาน? คุณหมายถึงประโยชน์? บางสิ่งที่อยู่ริมขอบอาจดูไร้ประโยชน์จนกว่าคุณจะต้องการ
AnthonyWJones

ดูเหมือนว่า Shadowing จะเข้ามาในภาพเฉพาะเมื่อเล่นเสมือนและใหม่เท่านั้น @ Stormenet กำลังอธิบายเฉพาะสิ่งที่ลบล้างเนื่องจากเป็นพฤติกรรมปกติของคลาสที่จะเรียกใช้เมธอดของตัวเองเว้นแต่จะเป็นเสมือน
Sumit Kapadia

6

ฉันคิดว่าความแตกต่างที่สำคัญคือเมื่อใช้การสร้างเงาคุณจะนำชื่อกลับมาใช้ใหม่เป็นหลักและเพิกเฉยต่อการใช้ซูเปอร์คลาส ด้วยการลบล้างคุณกำลังเปลี่ยนการใช้งาน แต่ไม่ใช่การเข้าถึงและลายเซ็น (เช่นประเภทพารามิเตอร์และการส่งคืน) ดูhttp://www.geekinterview.com/question_details/19331


5

Shadowing เป็นแนวคิด VB.NET ในภาษา C # Shadowing เรียกว่าการซ่อน มันซ่อนเมธอดคลาสที่ได้รับ ทำได้โดยใช้คำหลัก 'ใหม่'

คีย์เวิร์ด Override ใช้เพื่อจัดเตรียมการนำเมธอดคลาสพื้นฐานไปใช้ใหม่โดยสมบูรณ์ (ซึ่งมีเครื่องหมาย 'เสมือน') ในคลาสที่ได้รับ


คุณหมายถึง. มันซ่อนเมธอดคลาสที่ได้รับแทนที่จะใช้เมธอดคลาสฐาน เหรอ?
shaijut

0

โดยทั่วไปหากคุณมีสิ่งที่ต้องการด้านล่าง

Class A
{
}

Class B:A
{
}

A a = new B();

วิธีการใด ๆ ที่คุณเรียกใช้กับวัตถุ 'a' จะถูกสร้างขึ้นในประเภทของ 'a' (ในที่นี้คือประเภท 'A') แต่ถ้าคุณใช้วิธีการเดียวกันในคลาส B ที่มีอยู่แล้วในคลาส A คอมไพเลอร์จะ เตือนคุณให้ใช้คำหลัก "ใหม่" หากคุณใช้ "ใหม่" คำเตือนจะหายไป นอกเหนือจากนี้ไม่มีความแตกต่างระหว่างการใช้ "ใหม่" หรือไม่ใช้ในคลาสที่สืบทอดมา

ในบางสถานการณ์คุณอาจต้องเรียกใช้เมธอดของคลาสอ้างอิงที่อินสแตนซ์เฉพาะถืออยู่ในขณะนั้นแทนที่จะเรียกเมธอดในประเภทอ็อบเจ็กต์ ในกรณีข้างต้นการอ้างอิงถือเป็น 'B' แต่ประเภทคือ 'A' ดังนั้นหากคุณต้องการให้การเรียกใช้เมธอดเกิดขึ้นที่ 'B' คุณจึงใช้ Virtual และแทนที่เพื่อให้บรรลุสิ่งนี้

หวังว่านี่จะช่วยได้ ...

Daniel Sandeep


0

หากมีบางกรณีที่คุณไม่สามารถแก้ไขเนื้อหาของคลาสได้สมมติว่าAคุณต้องการใช้วิธีการบางอย่างพร้อมกับคุณมีวิธีการที่ชื่อเป็นเรื่องธรรมดาคุณสามารถใช้วิธีการของคุณเองโดยใช้newคีย์เวิร์ด

ประเด็นสำคัญคือการใช้ว่าทั้งการอ้างอิงและวัตถุต้องเป็นประเภทเดียวกัน

class A
{
    public void Test()
    {
        Console.WriteLine("base");
    }
}

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

    public static void Main(string[] args)
    {
        A a = new A();
        B aa = new B();
        a.Test();
        aa.Test();
    }
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.