มีวิธีใดในการลบล้างเมธอดที่ไม่ใช่เสมือนจริงหรือไม่? หรือสิ่งที่ให้ผลลัพธ์ที่คล้ายกัน (นอกเหนือจากการสร้าง 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());
นอกจากนี้คุณยังต้องตรวจสอบให้แน่ใจว่าตัวปรับการเข้าถึงนั้นเหมือนกันมิฉะนั้นคุณจะไม่ได้รับการสืบทอดลงมา ถ้าอีกสืบทอดชั้นจากคำหลักในจะไม่ส่งผลกระทบต่อวัตถุที่สืบทอดจากมันเว้นแต่ปรับปรุงการเข้าถึงจะเหมือนกันClass1
new
Class1
หากตัวแก้ไขการเข้าถึงไม่เหมือนกัน:
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()
(เอาชนะ)
คุณสามารถสร้างอินสแตนซ์คลาส (ที่ได้มา) ได้ตามต้องการ (เช่นด้วยการใช้งานพื้นฐานหรือการใช้การทดสอบหน่วย)