2010-09-17 30 views
5

Đại biểu của tôi dường như không chấp nhận một lớp con, tôi nghĩ một ví dụ là dễ nhất.Đại biểu không chấp nhận lớp con?

public class A 
{ 
    public A() { } 
} 

public class B : A 
{ 
    public B() { } 
} 

public class Program 
{ 
    private delegate void CallBack(A a); 
    private static CallBack callBack = new CallBack(Test); 

    public Main(string[] args) 
    { 
      callBack(new B()); 
    } 

    private static void Test(A a) 
    { 
      Console.WriteLine("Test()");  
    } 

    // Compilation error occurs if Test becomes: 
    private static void Test(B a) 
    { 
      Console.WriteLine("Test()"); 
    } 
} 

Khi tôi thay đổi Kiểm tra để chấp nhận B nó sẽ phát ra lỗi biên dịch. Không phải là điều này lẻ bởi vì B mở rộng A?

biên dịch lỗi:

Không quá tải cho thử nghiệm phù hợp với Callback

Có cách nào để làm cho đại biểu của tôi chấp nhận một lớp học kéo dài A?

+0

Có ném ngoại lệ hoặc không biên soạn không? – Elisha

+1

Điều đó không nên gây ra sự cố. Bạn có thể dán mã đã thay đổi ở đây không? Chỉ cần biết làm thế nào bạn đang thay đổi nó? –

+0

@Mamta Dalal - Đã chỉnh sửa câu hỏi của tôi, hy vọng điều này sẽ làm rõ hơn một chút. – Kevin

Trả lời

3

Nó không phải là lẻ bởi vì nếu bạn có một đối tượng của lớp C kéo dài A, nó sẽ không có ý nghĩa để vượt qua Test() nếu nó chỉ chấp nhận B. Bất kỳ phương pháp nào được sử dụng cho số Callback đều phải chấp nhận bất kỳA, không chỉ là một lớp con cụ thể. Bạn cần thay đổi chữ ký ủy quyền Callback để chấp nhận B nếu bạn muốn Test() chấp nhận B.

class C : A {}; 

Callback callback = Test; 

callback(new C()); //what if Test() accepted B??? 
+0

Vì vậy, lựa chọn duy nhất của tôi sẽ là đưa 'A' vào' B' bên trong 'Test'? – Kevin

+0

@Kevin: Không, đó không phải là lựa chọn duy nhất. Một tùy chọn tốt hơn là khai báo delegate là 'void CallBack (B b)'. Bạn vẫn có thể sử dụng phương thức 'Test (A a)'. – Timwi

+0

@Timwi - Tôi không thể làm điều đó tôi sợ. Tôi đã có một lớp 'Sự kiện' mà mọi người có thể mở rộng và chuyển cho đại biểu. – Kevin

-1

C# đại biểu support both covariance and contravariance, vì vậy việc này sẽ hiệu quả.

Sự cố là quá tải.

// this delegate supports contravariance - and subclass of A should work 
delegate void CallBack(A a); 

// however this can't pick up either Test because both could be used 
static CallBack callBack = new CallBack(Test); 

nào quá tải phương pháp chữ ký (Test(A a) hoặc Test(B b)) đã được giải quyết tại thời gian biên dịch - tuy nhiên cả hai có thể áp dụng, vì vậy một lỗi được ném.

Bạn có thể tránh điều này bằng cách tách ra khỏi sự quá tải:

static void TestA(A a) 
{ 
     Console.WriteLine("Test(a)");  
} 

// Compilation error occurs if Test becomes: 
static void TestB(B a) 
{ 
     Console.WriteLine("Test(b)"); 
} 

// this is valid because it's an exact match 
static CallBack callBackA = new CallBack(TestA); 

// this is valid because delegates support contravariance 
static CallBack callBackB = new CallBack(TestB); 

Trong cả hai trường hợp, bạn có thể vượt qua một B:

// B is subclass of A, so can be passed to TestA 
callBackA(new B()); 

// CallBack supports contravariance, so can call TestB 
callBackB(new B()); 

Cho rằng bạn có contravariance này, tại sao bạn cần quá tải ?

+0

Tôi đang cố gắng tạo lại mô hình sự kiện Flash trong C# :) - Cảm ơn bạn đã trả lời! Vấn đề lớn nhất là người dùng cần có khả năng mở rộng lớp 'Event' của tôi và chuyển lớp của họ cho đại biểu. – Kevin

+0

contravariance chỉ hoạt động khi đối số là loại ** ít được bắt nguồn ** hơn so với đối số trong khai báo đại biểu. Bạn không bao giờ có thể gán một phương thức lấy một đối số 'B' cho đại biểu mà bạn đã khai báo. Mặt khác, nếu đại biểu được khai báo tham gia một đối số 'B', bạn sẽ có thể thêm một phương thức đối số' A' –

1

Khá dễ hiểu.Bây giờ chúng ta có:

class A { } 
class B : A { } 

Kịch bản 1 vào đầu

public delegate void CallBack(A a); 
public void Test(A a) { } 
CallBack cb = new CallBack(Test); 
cb(new A()); //good and easy usage 

Kịch bản 2CallBack(A a)Test(B b)

//compile error, because Test(B b) has a smaller argument scope than CallBack 
//CallBack cb = new CallBack(Test); 

Kịch bản 3CallBack(B b)Test(A a)

CallBack cb = new CallBack(Test); 
cb(new A()); //no error, becasue B can convert to A 
+0

Cảm ơn bạn đã xóa nó. Tôi đoán tôi sẽ phải bỏ 'A' thành' B' rồi, quá tệ .. – Kevin

8

Đây có phải là điều lạ vì B mở rộng A?

Bạn có ý tưởng đúng, nhưng theo hướng sai. Hãy xem xét một ví dụ dễ hiểu hơn về lý do:

class Animal {} 
class Reptile : Animal {} 
class Snake : Reptile {} 
class Mammal : Animal {} 
class Tiger : Mammal {} 
class Giraffe : Mammal {} 
delegate void D(Mammal m); 
static void DoAnimal(Animal a) {} 
static void DoMammal(Mammal m) {} 
static void DoTiger(Tiger t) {} 

D dm = DoMammal; 
dm(new Tiger()); 

Điều đó rõ ràng là hợp pháp. dm cần phải là một phương pháp mà có một động vật có vú, và nó được.

D dt = DoTiger; 
dt(new Giraffe()); 

Điều đó rõ ràng là bất hợp pháp. Bạn không thể chỉ định một phương pháp đưa một con hổ đến một đại biểu có động vật có vú, bởi vì một đại biểu có động vật có vú có thể lấy bất kỳ động vật có vú nào, không chỉ là một con hổ. Nếu điều này là hợp pháp thì nó sẽ có thể vượt qua một con hươu cao cổ với một phương pháp mà có một con hổ.

Điều này thì sao?

D da = DoAnimal; 
da(new Giraffe()); 

Tốt thôi. da là một đại biểu cho một phương pháp mà có bất kỳ động vật có vú. Một phương pháp mà có bất kỳ động vật rõ ràng cũng có bất kỳ động vật có vú. Bạn có thể gán DoAnimal (Động vật) cho một đại biểu D (Mammal) vì động vật có vú kéo dài động vật. Bạn thấy bây giờ làm thế nào bạn có hướng mở rộng về phía sau?

loại Return về công tác Mặt khác theo cách mà bạn nghĩ rằng họ làm:

delegate Mammal F(); 
static Animal GetAnimal() {...} 
static Mammal GetMammal() {...} 
static Tiger GetTiger() {...} 

F fm = GetMammal; 
Mammal m = fm(); 

Không vấn đề gì đó.

F ft = GetTiger; 
Mammal t = ft(); 

Không vấn đề gì ở đó; GetTiger trả về một con hổ, vì vậy bạn có thể gán nó cho một đại biểu yêu cầu mục tiêu của nó trả về một động vật có vú.

F fa = GetAnimal; 
Mammal a = fa(); 

Điều đó không tốt. GetAnimal có thể trả về một con rắn, và bây giờ bạn có một biến gõ là động vật có vú có chứa một con rắn. Điều này là bất hợp pháp.

Tính năng này được gọi là "hiệp phương sai và đối nghịch của các chuyển đổi nhóm thành viên" và được giới thiệu trong C# 2.0. Để biết thêm thông tin về chủ đề này, hãy xem bài viết của tôi về bài viết đó:

http://blogs.msdn.com/b/ericlippert/archive/2007/10/19/covariance-and-contravariance-in-c-part-three-member-group-conversion-variance.aspx

Các vấn đề liên quan