เมื่อไม่นานมานี้ฉันได้ทำงานกับผู้ให้บริการอย่างเป็นธรรมและฉันได้พบกับสถานการณ์ที่น่าสนใจที่ฉันต้องการมีคลาสนามธรรมที่มีวิธีแบบสแตติกแบบนามธรรม ฉันอ่านบทความสองสามข้อเกี่ยวกับหัวข้อและมันก็สมเหตุสมผล แต่มีคำอธิบายที่ชัดเจนหรือไม่?
เมื่อไม่นานมานี้ฉันได้ทำงานกับผู้ให้บริการอย่างเป็นธรรมและฉันได้พบกับสถานการณ์ที่น่าสนใจที่ฉันต้องการมีคลาสนามธรรมที่มีวิธีแบบสแตติกแบบนามธรรม ฉันอ่านบทความสองสามข้อเกี่ยวกับหัวข้อและมันก็สมเหตุสมผล แต่มีคำอธิบายที่ชัดเจนหรือไม่?
คำตอบ:
วิธีการคงที่ไม่ได้ยกตัวอย่างเช่นนี้พวกเขาก็ใช้ได้โดยไม่มีการอ้างอิงวัตถุ
การเรียกใช้เมธอดแบบสแตติกทำผ่านชื่อคลาสไม่ใช่ผ่านการอ้างอิงวัตถุและรหัส Intermediate Language (IL) เพื่อเรียกใช้จะเรียกเมธอด abstract ผ่านชื่อของคลาสที่กำหนดไว้ไม่จำเป็นต้องเป็นชื่อของ ชั้นเรียนที่คุณใช้
ให้ฉันแสดงตัวอย่าง
ด้วยรหัสต่อไปนี้:
public class A
{
public static void Test()
{
}
}
public class B : A
{
}
ถ้าคุณโทรหา B.Test แบบนี้:
class Program
{
static void Main(string[] args)
{
B.Test();
}
}
จากนั้นโค้ดจริงภายในเมธอด Main จะเป็นดังนี้:
.entrypoint
.maxstack 8
L0000: nop
L0001: call void ConsoleApplication1.A::Test()
L0006: nop
L0007: ret
อย่างที่คุณเห็นการโทรนั้นทำกับ A.Test เพราะเป็นคลาส A ที่กำหนดไว้และไม่ใช่การทดสอบ B.Test แม้ว่าคุณจะสามารถเขียนรหัสได้
หากคุณมีประเภทคลาสเช่นใน Delphi ซึ่งคุณสามารถสร้างตัวแปรที่อ้างอิงถึงชนิดและไม่ใช่วัตถุคุณจะต้องใช้วิธีเสมือนและนามธรรมคงที่ (และคอนสตรัคเตอร์) แต่ก็ไม่มีให้ใช้และ ดังนั้นการโทรคงไม่ใช่เสมือนใน. NET
ฉันรู้ว่านักออกแบบ IL สามารถอนุญาตให้รวบรวมรหัสเพื่อโทรหา B.Test และแก้ไขการโทรได้ที่รันไทม์ แต่มันก็ยังไม่เป็นเสมือนจริงเพราะคุณจะต้องเขียนชื่อคลาสบางส่วนที่นั่น
วิธีเสมือนจริงและที่เป็นนามธรรมนั้นมีประโยชน์เฉพาะเมื่อคุณใช้ตัวแปรซึ่ง ณ รันไทม์สามารถมีวัตถุหลายประเภทและคุณต้องการเรียกวิธีการที่เหมาะสมสำหรับวัตถุปัจจุบันที่คุณมีอยู่ในตัวแปร ด้วยวิธีการคงที่คุณต้องผ่านชื่อคลาสต่อไปดังนั้นวิธีการที่แน่นอนในการเรียกเป็นที่รู้จักกันในเวลารวบรวมเพราะมันไม่สามารถและจะไม่เปลี่ยนแปลง
ดังนั้นวิธีการแบบคงที่เสมือน / นามธรรมจะไม่พร้อมใช้งานใน. NET
Test()
มันมีความหมายA
มากกว่าจะเป็นนามธรรมและอาจนิยามB
ได้ \
Car
ประเภทที่มีการคงเสมือนCreateFromDescription
การโรงงานแล้วรหัสที่ได้รับการยอมรับCar
ประเภททั่วไป -constrained T
สามารถโทรในการผลิตรถประเภทT.CreateFromDescription
T
โครงสร้างดังกล่าวสามารถรองรับได้ค่อนข้างดีภายใน CLR หากแต่ละประเภทที่กำหนดวิธีการดังกล่าวถือเป็นอินสแตนซ์ singleton แบบคงที่ของคลาสที่ซ้อนกันทั่วไปซึ่งมีวิธีการ "คงที่" เสมือนจริง
วิธีการคงที่ไม่สามารถสืบทอดหรือแทนที่และนั่นคือเหตุผลที่พวกเขาไม่สามารถเป็นนามธรรม เนื่องจากวิธีการแบบสแตติกถูกกำหนดไว้บนชนิดไม่ใช่อินสแตนซ์ของคลาสจึงต้องถูกเรียกอย่างชัดเจนในประเภทนั้น ดังนั้นเมื่อคุณต้องการเรียกใช้เมธอดบนคลาสย่อยคุณต้องใช้ชื่อเพื่อเรียกเมธอด สิ่งนี้ทำให้มรดกไม่เกี่ยวข้อง
สมมติว่าคุณทำได้โดยสืบทอดวิธีคงที่สักครู่ ลองนึกภาพสถานการณ์นี้:
public static class Base
{
public static virtual int GetNumber() { return 5; }
}
public static class Child1 : Base
{
public static override int GetNumber() { return 1; }
}
public static class Child2 : Base
{
public static override int GetNumber() { return 2; }
}
ถ้าคุณเรียก Base.GetNumber () วิธีการใดที่จะเรียก? คืนค่าใด มันค่อนข้างง่ายที่จะเห็นว่าโดยไม่ต้องสร้างอินสแตนซ์ของวัตถุการสืบทอดค่อนข้างยาก วิธีที่เป็นนามธรรมโดยไม่มีการสืบทอดเป็นเพียงวิธีการที่ไม่มีร่างกายดังนั้นจึงไม่สามารถเรียกได้
int DoSomething<T>() where T:Base {return T.GetNumber();}
สมมติว่าใครมี มันจะมีประโยชน์ถ้าDoSomething<Base>()
สามารถคืนได้ห้าในขณะที่DoSomething<Child2>()
จะคืนสอง ความสามารถดังกล่าวไม่เพียง แต่จะเป็นประโยชน์สำหรับตัวอย่างของเล่นเท่านั้น แต่ยังรวมถึงสิ่งอื่น ๆ เช่นclass Car {public static virtual Car Build(PurchaseOrder PO);}
ที่ทุกชั้นเรียนCar
จะต้องกำหนดวิธีการที่สามารถสร้างตัวอย่างที่ได้รับคำสั่งซื้อ
ผู้ตอบอีกคนหนึ่ง (McDowell) กล่าวว่า polymorphism นั้นใช้ได้กับอินสแตนซ์ของวัตถุเท่านั้น นั่นควรจะมีคุณสมบัติ; มีภาษาที่ใช้งานคลาสเป็นอินสแตนซ์ของประเภท "Class" หรือ "Metaclass" ภาษาเหล่านี้รองรับความหลากหลายสำหรับทั้งอินสแตนซ์และเมธอดคลาส (สแตติก)
C # เช่น Java และ C ++ ก่อนหน้านั้นไม่ใช่ภาษาดังกล่าว static
คำหลักที่ถูกนำมาใช้อย่างชัดเจนเพื่อแสดงว่าวิธีการที่เป็นแบบคงที่ถูกผูกไว้มากกว่าแบบไดนามิก / เสมือน
นี่คือสถานการณ์ที่จำเป็นต้องมีการสืบทอดสำหรับฟิลด์และเมธอดแบบสแตติกแน่นอน:
abstract class Animal
{
protected static string[] legs;
static Animal() {
legs=new string[0];
}
public static void printLegs()
{
foreach (string leg in legs) {
print(leg);
}
}
}
class Human: Animal
{
static Human() {
legs=new string[] {"left leg", "right leg"};
}
}
class Dog: Animal
{
static Dog() {
legs=new string[] {"left foreleg", "right foreleg", "left hindleg", "right hindleg"};
}
}
public static void main() {
Dog.printLegs();
Human.printLegs();
}
//what is the output?
//does each subclass get its own copy of the array "legs"?
legs
ควรเป็นคุณสมบัตินามธรรมแบบคงที่
เมื่อต้องการเพิ่มคำอธิบายก่อนหน้านี้การเรียกใช้เมธอดแบบสแตติกจะถูกผูกไว้กับวิธีการเฉพาะในเวลาคอมไพล์ซึ่งค่อนข้างออกกฎพฤติกรรม polymorphic
เราแทนที่เมธอดสแตติก (ในเดลฟาย) จริง ๆ แล้วมันค่อนข้างน่าเกลียด แต่ก็ใช้ได้ดีสำหรับความต้องการของเรา
เราใช้มันเพื่อให้คลาสสามารถมีรายการของอ็อบเจ็กต์ที่มีอยู่โดยไม่มีอินสแตนซ์ของคลาสตัวอย่างเช่นเรามีเมธอดที่มีลักษณะดังนี้:
class function AvailableObjects: string; override;
begin
Result := 'Object1, Object2';
end;
มันน่าเกลียด แต่จำเป็นวิธีนี้เราสามารถสร้างอินสแตนซ์ของสิ่งที่ต้องการแทนการให้ทุกคลาสสร้างอินสแตนซ์เพียงเพื่อค้นหาออบเจ็กต์ที่มีอยู่
นี่เป็นตัวอย่างง่ายๆ แต่แอปพลิเคชันเองเป็นแอปพลิเคชันไคลเอนต์เซิร์ฟเวอร์ที่มีคลาสทั้งหมดที่มีในเซิร์ฟเวอร์เดียวและไคลเอนต์ที่แตกต่างกันซึ่งอาจไม่ต้องการทุกสิ่งที่เซิร์ฟเวอร์มีและจะไม่ต้องการอินสแตนซ์ของวัตถุ
ดังนั้นการบำรุงรักษานี้ง่ายกว่าการมีแอปพลิเคชั่นเซิร์ฟเวอร์ที่แตกต่างกันหนึ่งรายการสำหรับไคลเอนต์แต่ละตัว
หวังว่าตัวอย่างจะชัดเจน
วิธีนามธรรมเป็นเสมือนโดยปริยาย วิธีการแบบนามธรรมต้องการอินสแตนซ์ แต่วิธีการแบบสแตติกไม่มีอินสแตนซ์ ดังนั้นคุณสามารถมีวิธีการคงที่ในระดับนามธรรมมันก็ไม่สามารถเป็นนามธรรมคงที่ (หรือนามธรรมคงที่)