2009-01-26 2 views
6

Có giống như quá tải, nếu không, bạn có thể vui lòng cung cấp và ví dụ về từng trường hợp trong C#Điều gì là - Đơn và nhiều công văn (liên quan đến .NET)?

Tôi đã đọc câu trả lời cho câu hỏi tương tự được hỏi trong SO ... tôi không hiểu câu trả lời được đăng lên đó .

câu hỏi tương tự hỏi here

EDIT: Với sự mới "năng động" từ khoá trong C# 4.0 ... điều này sẽ làm cho ngôn ngữ "đa văn" kích hoạt?

Trả lời

5

C# sử dụng công văn đơn, bao gồm các phương thức quá tải. Khi bạn có mã

stringBuilder.Append(parameter); 

trình điều phối xem tất cả các phương pháp được xác định trên lớp của chuỗiBuilder và tìm đúng phương pháp.

Để có ví dụ về nhiều công văn, hãy xem Prolog (đây là cái đầu tiên tôi có thể nghĩ đến). Bạn có thể xác định hàm trong prolog như vậy:

func(Arg1, Arg2) :- ....body.... 

Điều này không được định nghĩa trong bất kỳ lớp nào nhưng trong phạm vi toàn cầu. Sau đó, bạn có thể gọi func(Arg1, Arg2) trên bất kỳ hai đối số nào và chức năng này sẽ được gọi. Nếu bạn muốn một cái gì đó giống như quá tải, bạn phải xác nhận các loại đối số bên trong hàm, và định nghĩa nó nhiều lần:

func(Arg1, Arg2) :- is_number(Arg1), is_string(Arg2), ....body.... 
func(Arg1, Arg2) :- is_string(Arg1), is_list(Arg2), ....body.... 
func(Arg1, Arg2) :- is_number(Arg1), is_list(Arg2), ....body.... 

Sau đó, bất kỳ hai loại đối số bạn sẽ gửi sẽ cả được kiểm tra - đó là công văn nhiều phần.

Trong ngắn hạn, một công văn chỉ nhìn vào các phương thức được xác định trên đối số đầu tiên (trong ví dụ đầu tiên của chúng ta, stringBuilder), sau đó giải quyết quá tải chính xác để gọi bằng các đối số khác. Nhiều công văn có các phương thức/hàm được định nghĩa trong phạm vi toàn cục và xử lý tất cả các đối số giống nhau trong quá trình phân giải quá tải.

Tôi hy vọng tôi tự làm rõ, đây là một chủ đề khá khó khăn.


Cập nhật: Tôi quên đề cập đến, nhiều công văn diễn ra khi chạy trong khi công văn đơn lẻ diễn ra lúc biên dịch.
Cập nhật # 2: Rõ ràng, điều đó không đúng.

+0

Một trong những ngôn ngữ yêu thích của tôi. Hy vọng tôi có các vị từ, mặc dù tôi đã không sử dụng nó trong nhiều năm. – configurator

+0

Cả hai đơn và nhiều công văn có thể được thực hiện tại một trong hai thời gian biên dịch hoặc thời gian chạy. Công văn động là thuật ngữ cho mã ràng buộc khi chạy. –

+0

@Bill: có, nhưng các loại được sử dụng cho nhiều công văn là các kiểu thời gian chạy và các loại được sử dụng cho một công văn (đối với các đối số) là kiểu thời gian biên dịch. Tôi có nhầm ở đây không? – configurator

1

Đơn/nhiều công văn là một loại quá tải thời gian chạy. Công văn đơn thường được gọi là các hàm ảo. Hàm chính xác được gọi khi bạn gọi hàm ảo được quyết định dựa trên kiểu thời gian chạy của một đối tượng. Công văn kép giống nhau, được mở rộng để làm việc với hai đối tượng - thường là thông số this và tham số thứ hai. Bạn có thể thực hiện điều này mà không gặp quá nhiều khó khăn khi sử dụng Mẫu Vistor. Nhiều công văn tiếp tục mở rộng khái niệm này đến nhiều tham số hơn, nhưng khó thực hiện hơn nhiều trong các ngôn ngữ như C# (không phải là nó không thể được thực hiện, nó chỉ là khó). Một số ngôn ngữ thực hiện khả năng này ngay trên hộp.

ví dụ: trong .NET, hàm ToString() là một ví dụ về đơn công văn

// Single dispatch 
Object o = GetSomeObject(); // Return SomeType casted to Object. 
o.ToString(); // Call SomeType::ToString instead of just Object::ToString 

// Double dispatch (this version won't work in C#) 
Shape s1 = GetSquare(); 
Shape s2 = GetCircle(); 
s1.Intersects(s2); // If C# supported double dispatch, this would call Square::Intersects(Circle) not Square::Intersects(Shape) 
2

Nhiều công văn liên quan đến quá tải phương thức nhưng không giống nhau. Trước đây là một quyết định thời gian chạy động, sau này là một quyết định compiletime tĩnh. Rõ ràng là một lợi ích linh hoạt, nhưng chi phí hiệu suất cho đa công văn do đó.

AFAIK một ngôn ngữ có thể hỗ trợ một trong hai, nhưng không phải cả hai, mặc dù cả hai có thể được mô phỏng (đa công văn có thể được mô phỏng với khách truy cập). C# xác định kiểu dữ liệu tại thời gian biên dịch và do đó không phải là một ngôn ngữ đa công văn, vì vậy không có ví dụ nào là có thể.

(caveat: Tôi không phải là 100% về vấn đề này)


phụ lục: thực wikipedia có article về vấn đề này mà có vẻ khá kỹ lưỡng, ooh và một ví dụ LISP hữu ích

+0

Với từ khóa "động" mới trong C# 4.0 ... điều này có làm cho ngôn ngữ "đa công văn" được bật không? – Developer

+0

@Nick, không chính xác, nhưng nó sẽ là khá gần. – configurator

+0

chính xác vì vậy tôi tin rằng – annakata

1

Trong OO languaes đoạn Phúc Âm:

SomeType b; 
a = b.Foo(c, d) 

Nghĩa là đối tượng b đang được chuyển thông điệp Foo với các thông số c và d. Công văn đơn có nghĩa là chỉ MỘT khía cạnh của hệ thống này chịu trách nhiệm xác định thời gian chạy mà (có thể là nhiều) phương thức Foo thực sự sẽ được gọi.

Mô hình java, C# và hầu hết các ngôn ngữ OO khác sử dụng là chỉ có kiểu thời gian chạy 'b' mới được 'quyết định' gọi phương thức thực tế là gì. Vì vậy, nếu bạn có hai triển khai của SomeType, cả hai đều cung cấp một triển khai Foo khác nhau thì quyết định sử dụng chỉ dựa trên loại b nào xảy ra tại thời điểm đó. NẾU có nhiều quá tải của Foo thì quyết định sử dụng quá tải là quyết định thời gian biên dịch chỉ dựa trên thời gian biên dịch biết các loại b, c và d.

Điều này sau đó là công văn đơn lẻ, điểm duy nhất của sự lựa chọn là hệ thống kiểu được liên kết với b.

Nhiều công văn sẽ, trong thời gian chạy, cho phép các loại thời gian chạy của b, c và d để quyết định phương thức cần gọi (quyết định này chắc chắn phức tạp hơn). Trong một hệ thống năng động hơn, nơi khái niệm về các loại được xác định tốt là chất lỏng hơn (nói một hệ thống dựa trên nguyên mẫu chứ không phải mô hình kế thừa bạn biết từ C++/java/C#). thay vào đó là hoàn toàn đến các trường hợp thực tế b, c và d.

0
#include <iostream> 

class Pet { 
}; 

class Cat: public Pet { 
}; 

class Dog: public Pet { 
}; 

class Human { 
}; 

class Man : public Human { 
     public: 
       void Kick(Cat& victim); 
       void Kick(Dog& victim); 
}; 

class Woman : public Human { 
     public: 
       void Kick(Cat& victim); 
       void Kick(Dog& victim); 
}; 

void Man::Kick(Cat& victim) { 
     std::cout << "Meow!!!" << std::endl; 
} 

void Woman::Kick(Cat& victim) { 
     std::cout << "I won't kick a cat" << std::endl; 
} 

void Man::Kick(Dog& victim) { 
     std::cout << "I won't kick a dog" << std::endl; 
} 

void Woman::Kick(Dog& victim) { 
     std::cout << "Woof!!!" << std::endl; 
} 

int main(int argc, char** argv) { 
     Man kicker; 
     Dog victim; 
     Pet zoo[] = { victim }; 
     kicker.Kick(victim); 
//  kicker.Kick(zoo[0]); // No multimethods 
     return 0; 
} 

Như bây giờ, C++ không thể tìm ra trong thời gian chạy cho dù một Pet thực sự là một Cat hoặc một Dog.

Nếu có một số cách để làm điều đó trong thời gian chạy (để mã ở trên biên dịch với dòng nhận xét không được chú ý), C++ sẽ được cho là hỗ trợ nhiều công văn hoặc đa phương thức.

5

Nhiều công văn là một "hình thức" quá tải ...

Ví dụ, C# là công văn đơn bởi vì nếu công trình ra những phương pháp nào được gọi dựa trên chỉ có một đối số, các "này" con trỏ. Khi bạn có một cái gì đó như thế này:

Base base= new Derived(); 
base.DoSomething(); 

phương thức Derived.DoSomething được gọi ngay cả khi bạn gọi nó thông qua con trỏ cơ sở.Bây giờ, nếu chúng ta có những điều sau đây:

class Derived : Base 
{ 
    public override void Process(Stream stream); 
    public override void Process(FileStream stream); 
    public override void Process(MemoryStream stream); 
} 

Và chúng tôi làm điều này:

Stream stream= new MemoryStream(...); 
Base b= new Derived(); 
b.Process(stream); 

Sau đó chúng tôi sẽ gọi Process (Stream) phương pháp từ Derived như C# làm một công văn duy nhất trên con trỏ đối tượng (b) và sau đó sử dụng thông tin thời gian biên dịch để quyết định phương thức cần gọi. Mặc dù luồng là một MemoryStream một hệ thống gửi đơn sẽ bỏ qua điều này.

Trong hệ thống đa công văn, con trỏ đối tượng sẽ được xem xét (như trong C#) VÀ kiểu thời gian chạy của đối số sẽ được kiểm tra. Trong ví dụ trên, vì luồng thực sự là một MemoryStream, hệ thống sẽ gọi phương thức Quy trình (MemoryStream).

+0

Trong khi giải thích là chính xác, có một lỗi trong mã; b.Process không thể được gọi nếu nó chỉ được định nghĩa trong Derived. – configurator

+1

Đây là một ví dụ tuyệt vời! –

0

Trong đa phương tiện C# 4.0 được bật với từ khóa động mới:

using System; namespace Ví dụ { lớp Wheel { public void RepairWhell() {}}

class Chassis 
{ 
    public void RepairChassis() { } 
} 

class Engine 
{ 
    public void RepairEngine() { } 
} 

class CarWorkshop 
{ 
    public string Repair(Wheel value) 
    { 
     value.RepairWhell(); 
     return "wheel repaired"; 
    } 
    public string Repair(Chassis value) 
    { 
     value.RepairChassis(); 
     return "chassis repaired"; 
    } 
    public string Repair(Engine value) 
    { 
     value.RepairEngine(); 
     return "engine repaired"; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     dynamic carWorkshop = new CarWorkshop(); 

     var whell = new Wheel(); 
     var chassis = new Chassis(); 
     var engine = new Engine(); 

     Console.WriteLine(carWorkshop.Repair(whell)); 
     Console.WriteLine(carWorkshop.Repair(chassis)); 
     Console.WriteLine(carWorkshop.Repair(engine)); 
     Console.ReadLine(); 
    } 
} 

}

động giới thiệu multimethods, một mô hình rất mạnh mẽ, trong C#.

0

Xin lỗi, ví dụ tôi đưa ra trước khi nó bị nhầm lẫn. Không phải là phiên bản chính xác:

class Wheel 
{ 
    public void RepairWhell() { } 
} 

class Chassis 
{ 
    public void RepairChassis() { } 
} 

class Engine 
{ 
    public void RepairEngine() { } 
} 

class CarWorkshop 
{ 
    public string Repair(Wheel value) 
    { 
     value.RepairWhell(); 
     return "wheel repaired"; 
    } 
    public string Repair(Chassis value) 
    { 
     value.RepairChassis(); 
     return "chassis repaired"; 
    } 
    public string Repair(Engine value) 
    { 
     value.RepairEngine(); 
     return "engine repaired"; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var carWorkshop = new CarWorkshop(); 

     dynamic whell = new Wheel(); 
     dynamic chassis = new Chassis(); 
     dynamic engine = new Engine(); 

     Console.WriteLine(carWorkshop.Repair(whell)); 
     Console.WriteLine(carWorkshop.Repair(chassis)); 
     Console.WriteLine(carWorkshop.Repair(engine)); 
     Console.ReadLine(); 
    } 
} 

Vì vậy, anwer là có. C# cung cấp nhiều công văn.

0

Bạn có thể sử dụng từ khóa động để triển khai đa công văn trong C#.

interface IA { } 
interface IB { } 
class CA1 : IA {} 
class CA2 : IA {} 
class CA11 : CA1 {} 
class CB1 : IB {} 
class CB2 : IB {} 

class MD 
{ 
    public enum X { X } ; 
    public static void Foo(IA a, IB b, X dispatch = X.X) { Foo((dynamic)a, (dynamic)b); } 
    static void Foo(IA a, IB b) { Console.WriteLine("IA IB"); } 
    static void Foo(CA1 a, CB1 b) { Console.WriteLine("CA1 CB1"); } 
    static void Foo(CA2 a, CB1 b) { Console.WriteLine("CA2 CB1"); } 
    static void Foo(CA1 a, CB2 b) { Console.WriteLine("CA1 CB2"); } 
    static void Foo(CA2 a, CB2 b) { Console.WriteLine("CA2 CB2"); } 
    static void Foo(CA11 a, CB2 b) { Console.WriteLine("CA11 CB2"); } 
} 
class Program 
{ 
    static void Main(string[] args) 
    { 
     var a1 = new CA1(); 
     var a11 = new CA11(); 
     var a2 = new CA2(); 
     var b1 = new CB1(); 
     var b2 = new CB2(); 
     MD.Foo(a1, b1); 
     MD.Foo(a2, b1); 
     MD.Foo(a1, b2); 
     MD.Foo(a2, b2); 
     MD.Foo(a11, b1); 
     MD.Foo(a11, b2); 
    } 
} 

Độ phân giải của Foo ((năng động) a, (động) b)) được thực hiện tại thời gian chạy, và chọn một trong những phương pháp Foo quá tải dựa trên các loại bê tông của 'a' và 'b'.