簡體   English   中英

Delegate不接受子類?

[英]Delegate doesn't accept subclass?

我的代表似乎不接受一個子類,我認為一個例子是最簡單的。

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()");
     }
 }

當我將Test更改為接受B它會拋出編譯錯誤。 這不奇怪,因為B延伸A

編譯錯誤

測試沒有重載匹配回調

有沒有辦法讓我的委托接受一個擴展A的類?

這不奇怪,因為B延伸A?

你有正確的想法,但方向錯誤。 讓我們考慮一個更容易推理的例子:

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());

這顯然是合法的。 dm需要是一個采用哺乳動物的方法,它是。

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

這顯然是非法的。 你不能指定一種方法將老虎帶給一個哺乳動物的代表,因為一個哺乳動物的代表可以接受任何哺乳動物,而不僅僅是老虎。 如果這是合法的,那么就可以將長頸鹿傳遞給一只需虎的方法。

那這個呢?

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

沒關系。 da是一種接受任何哺乳動物的方法的代表。 一種明確攝取任何動物的方法也需要任何哺乳動物。 您可以將DoAnimal(動物)分配給委托D(哺乳動物),因為Mammal擴展了Animal。 你現在看到你如何向后延伸方向?

另一方面, 返回類型以您認為的方式工作:

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

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

沒問題。

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

沒問題; GetTiger返回一個Tiger,因此您可以將其分配給需要其目標返回哺乳動物的委托。

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

那不好。 GetAnimal可能會返回一個Snake,現在你有一個變量類型為Mammal,其中包含一個Snake。 這必須是非法的。

此功能稱為“成員組轉換的協方差和逆變”,它在C#2.0中引入。 有關此主題的更多信息,請參閱我的文章:

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

這並不奇怪,因為如果你有一個擴展A C類對象,如果它只接受一個B則傳遞給Test()是沒有意義的。 用於Callback任何方法都必須接受任何 A ,而不僅僅是特定的子類。 您需要更改Callback委托簽名接受B ,如果你想Test()接受B為好。

class C : A {};

Callback callback = Test;

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

這很容易理解。 現在我們有:

class A { }
class B : A { }

場景1開頭

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

場景2 CallBack(A a)Test(B b)

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

場景3回 CallBack(B b)Test(A a)

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

C#代表支持協方差和逆變 ,所以這應該工作。

問題是過載。

 // 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);

必須在編譯時解析哪個重載方法簽名( Test(A a)Test(B b) ) - 但兩者都可以應用,因此會引發錯誤。

您可以通過拆分重載來避免這種情況:

 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);

無論哪種情況,你都可以通過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());

鑒於你有這種逆轉,你為什么需要重載?

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM