มีวิธีใดในการลบล้างเมธอดที่ไม่ใช่เสมือนจริงหรือไม่? หรือสิ่งที่ให้ผลลัพธ์ที่คล้ายกัน (นอกเหนือจากการสร้าง method ใหม่เพื่อเรียก method ที่ต้องการ)?
ฉันต้องการลบล้างวิธีการจากMicrosoft.Xna.Framework.Graphics.GraphicsDeviceโดยคำนึงถึงการทดสอบหน่วย
มีวิธีใดในการลบล้างเมธอดที่ไม่ใช่เสมือนจริงหรือไม่? หรือสิ่งที่ให้ผลลัพธ์ที่คล้ายกัน (นอกเหนือจากการสร้าง method ใหม่เพื่อเรียก method ที่ต้องการ)?
ฉันต้องการลบล้างวิธีการจากMicrosoft.Xna.Framework.Graphics.GraphicsDeviceโดยคำนึงถึงการทดสอบหน่วย
คำตอบ:
ไม่คุณไม่สามารถแทนที่เมธอดที่ไม่ใช่เสมือนได้ สิ่งที่ใกล้เคียงที่สุดที่คุณสามารถทำได้คือซ่อนวิธีการโดยการสร้างnewเมธอดที่มีชื่อเดียวกัน แต่ไม่แนะนำให้ทำเช่นนี้เนื่องจากเป็นการทำลายหลักการออกแบบที่ดี
แต่ถึงแม้การซ่อนเมธอดจะไม่ทำให้คุณมีเวลาในการประมวลผลการเรียกใช้เมธอดที่หลากหลายเหมือนการเรียกเมธอดเสมือนจริง ลองพิจารณาตัวอย่างนี้:
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");
    }
}
ในตัวอย่างนี้ทั้งเรียกไปพิมพ์วิธีการM Foo.Mดังที่คุณเห็นวิธีนี้ช่วยให้คุณสามารถใช้งานเมธอดใหม่ได้ตราบใดที่การอ้างอิงถึงอ็อบเจ็กต์นั้นเป็นประเภทที่ได้รับที่ถูกต้อง แต่การซ่อนเมธอดพื้นฐานจะทำให้ความหลากหลายแตกต่างกัน  
ฉันอยากจะแนะนำว่าอย่าซ่อนเมธอดพื้นฐานในลักษณะนี้
ฉันมักจะเข้าข้างผู้ที่ชื่นชอบพฤติกรรมเริ่มต้นของ C # ว่าวิธีการไม่ใช่เสมือนโดยค่าเริ่มต้น (ซึ่งตรงข้ามกับ Java) ฉันจะไปให้ไกลกว่านั้นและบอกว่าชั้นเรียนควรปิดผนึกโดยค่าเริ่มต้นด้วย การสืบทอดเป็นเรื่องยากที่จะออกแบบให้เหมาะสมและความจริงที่ว่ามีวิธีการที่ไม่ถูกทำเครื่องหมายให้เป็นเสมือนแสดงว่าผู้เขียนวิธีนั้นไม่เคยตั้งใจให้วิธีการนั้นถูกแทนที่
แก้ไข: "การจัดส่งแบบกำหนดเวลาดำเนินการ" :
สิ่งที่ฉันหมายถึงนี้คือพฤติกรรมเริ่มต้นที่เกิดขึ้นในเวลาดำเนินการเมื่อคุณเรียกใช้วิธีการเสมือน สมมติว่าในตัวอย่างโค้ดก่อนหน้าของฉันแทนที่จะกำหนดเมธอดที่ไม่ใช่เสมือนฉันได้กำหนดเมธอดเสมือนจริงและวิธีการแทนที่จริงด้วย
ถ้าฉันจะเรียกb.Fooในกรณีนั้น CLR จะกำหนดประเภทของวัตถุที่การbอ้างอิงชี้ไปอย่างถูกต้องBarและจะส่งการเรียกไปMอย่างเหมาะสม
ไม่คุณทำไม่ได้
คุณสามารถแทนที่วิธีการเสมือนเท่านั้น - ดูMSDN ที่นี่ :
ใน C # คลาสที่ได้รับสามารถมีเมธอดที่มีชื่อเดียวกับเมธอดคลาสพื้นฐาน
- ต้องกำหนดเมธอดคลาสฐานเสมือน
ถ้าคลาสพื้นฐานไม่ได้ปิดผนึกคุณสามารถสืบทอดจากคลาสและเขียนเมธอดใหม่ที่ซ่อนคลาสพื้นฐาน (ใช้คีย์เวิร์ด "new" ในการประกาศเมธอด) ไม่เช่นนั้นคุณจะไม่สามารถลบล้างได้เนื่องจากไม่เคยมีผู้เขียนดั้งเดิมตั้งใจที่จะลบล้างดังนั้นเหตุใดจึงไม่เสมือนจริง
ฉันคิดว่าคุณกำลังทำงานหนักเกินไปและเอาชนะความสับสนการโอเวอร์โหลดหมายความว่าคุณมีสองวิธีขึ้นไปที่มีชื่อเดียวกัน แต่ชุดพารามิเตอร์ต่างกันในขณะที่การลบล้างหมายความว่าคุณมีการนำไปใช้งานที่แตกต่างกันสำหรับวิธีการในคลาสที่ได้รับ (ดังนั้นการแทนที่หรือปรับเปลี่ยนพฤติกรรม ในคลาสพื้นฐาน)
ถ้าเมธอดเป็นเสมือนคุณสามารถแทนที่ได้โดยใช้คีย์เวิร์ดแทนที่ในคลาสที่ได้รับมา อย่างไรก็ตามวิธีการที่ไม่ใช่เสมือนสามารถซ่อนการใช้งานพื้นฐานได้โดยใช้คีย์เวิร์ดใหม่แทนคีย์เวิร์ดแทนที่ เส้นทางที่ไม่ใช่เสมือนจะไม่มีประโยชน์หากผู้เรียกเข้าถึงเมธอดผ่านตัวแปรที่พิมพ์เป็นประเภทฐานเนื่องจากคอมไพเลอร์จะใช้การจัดส่งแบบคงที่ไปยังเมธอดพื้นฐาน (หมายถึงรหัสในคลาสที่ได้รับมาของคุณจะไม่ถูกเรียก)
ไม่มีสิ่งใดที่ป้องกันไม่ให้คุณเพิ่มการโอเวอร์โหลดลงในคลาสที่มีอยู่ แต่มีเพียงรหัสที่รู้เกี่ยวกับคลาสของคุณเท่านั้นที่จะสามารถเข้าถึงได้
คุณไม่สามารถแทนที่เมธอดที่ไม่ใช่เสมือนของคลาสใด ๆ ใน C # (โดยไม่ต้องแฮ็ก CLR) แต่คุณสามารถแทนที่วิธีการใด ๆ ของอินเทอร์เฟซที่คลาสใช้ ถือว่าเราไม่มีการปิดผนึก
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();
    }
}
และนี่คือการสาธิต
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();
    }
}
ในกรณีที่คุณรับช่วงจากคลาสที่ไม่ได้มาคุณสามารถสร้างคลาสซูเปอร์ที่เป็นนามธรรมและสืบทอดจากดาวน์สตรีมแทนได้
มีวิธีใดในการลบล้างเมธอดที่ไม่ใช่เสมือนจริงหรือไม่? หรือสิ่งที่ให้ผลลัพธ์ที่คล้ายกัน (นอกเหนือจากการสร้าง method ใหม่เพื่อเรียก method ที่ต้องการ)?
คุณไม่สามารถแทนที่เมธอดที่ไม่ใช่เสมือน อย่างไรก็ตามคุณสามารถใช้newคีย์เวิร์ดตัวปรับแต่งเพื่อให้ได้ผลลัพธ์ที่คล้ายกัน:
class Class0
{
    public int Test()
    {
        return 0;
    }
}
class Class1 : Class0
{
    public new int Test()
    {
        return 1;
    }
}
. . .
// result of 1
Console.WriteLine(new Class1().Test());
นอกจากนี้คุณยังต้องตรวจสอบให้แน่ใจว่าตัวปรับการเข้าถึงนั้นเหมือนกันมิฉะนั้นคุณจะไม่ได้รับการสืบทอดลงมา ถ้าอีกสืบทอดชั้นจากคำหลักในจะไม่ส่งผลกระทบต่อวัตถุที่สืบทอดจากมันเว้นแต่ปรับปรุงการเข้าถึงจะเหมือนกันClass1newClass1
หากตัวแก้ไขการเข้าถึงไม่เหมือนกัน:
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());
... เมื่อเทียบกับถ้าปรับปรุงการเข้าถึงเป็นเหมือนเดิม:
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());
ดังที่ได้กล่าวไว้ในคำตอบก่อนหน้านี้ไม่ใช่หลักการออกแบบที่ดี
มีวิธีการบรรลุสิ่งนี้โดยใช้คลาสนามธรรมและวิธีนามธรรม
พิจารณา
Class Base
{
     void MethodToBeTested()
     {
        ...
     }
     void Method1()
     {
     }
     void Method2()
     {
     }
     ...
}
ตอนนี้หากคุณต้องการมี method MethodToBeTested () ที่แตกต่างกันให้เปลี่ยน Class Base เป็นคลาสนามธรรมและ Method MethodToBeTested () เป็นวิธีนามธรรม
abstract Class Base
{
     abstract void MethodToBeTested();
     void Method1()
     {
     }
     void Method2()
     {
     }
     ...
}
ด้วยโมฆะนามธรรม MethodToBeTested () มีปัญหา; การใช้งานหายไป
ดังนั้นสร้างclass DefaultBaseImplementation : Baseเพื่อให้มีการใช้งานเริ่มต้น
และสร้างอีกรายการclass UnitTestImplementation : Baseเพื่อให้มีการใช้งานการทดสอบหน่วย
ด้วยคลาสใหม่ 2 คลาสนี้ฟังก์ชันการทำงานของคลาสพื้นฐานสามารถถูกลบล้างได้
Class DefaultBaseImplementation : Base    
{
    override void MethodToBeTested()    
    {    
        //Base (default) implementation goes here    
    }
}
Class UnitTestImplementation : Base
{
    override void MethodToBeTested()    
    {    
        //Unit test implementation goes here    
    }
}
ตอนนี้คุณมี 2 คลาสการใช้ MethodToBeTested()(เอาชนะ)
คุณสามารถสร้างอินสแตนซ์คลาส (ที่ได้มา) ได้ตามต้องการ (เช่นด้วยการใช้งานพื้นฐานหรือการใช้การทดสอบหน่วย)