我的委托似乎不接受一个子类,我认为一个例子是最简单的。
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
?
答案 0 :(得分:9)
这不是奇怪的,因为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中引入的。有关此主题的更多信息,请参阅我的文章:
答案 1 :(得分:3)
这并不奇怪,因为如果你有C
类的对象扩展A
,如果它只接受Test()
则传递给B
是没有意义的。 1}}。用于Callback
的任何方法都必须接受任何 A
,而不仅仅是特定的子类。如果您希望Callback
同时接受B
,则需要更改Test()
代理签名以接受B
。
class C : A {};
Callback callback = Test;
callback(new C()); //what if Test() accepted B???
答案 2 :(得分:1)
这很容易理解。现在我们有:
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
答案 3 :(得分:-1)
C#委托support both covariance and contravariance,所以这应该有效。
问题是过载。
// 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());
鉴于你有这种逆转,你为什么需要重载?