函数的C#委托没有返回值,一个参数允许子类型(作为参数)

时间:2017-06-29 22:09:14

标签: c# inheritance delegates covariance contravariance

我正在寻找一个委托,该委托封装了一个不返回任何值的方法并采用一个参数,例如 Action< T> 可以,但不幸的是,该委托与将子类型作为参数的方法不匹配...

这就是我想要做的事情:

public class BaseType
{
    public BaseType()
    {

    }
}
public class ChildType : BaseType
{
    public ChildType()
    {

    }
}
public class Test
{

public delegate void CallBackHandler(BaseType p);

    public Test()
    {
        CallBackHandler clbk1 = callBackA;
        CallBackHandler clbk2 = callBackB;   //That line does not compile
                                             //->'No overload for 'callBackB' matches delegate 'Test.CallBackHandler'
    }

    private void callBackA(BaseType p)
    {

    }

    private void callBackB(ChildType p)
    {

    }
}

我读到了关于协方差,逆变等等...我知道这涉及到了类型铸造,但我对这一切都有点困惑......

我应该使用哪个代表来使我的代码有效?

3 个答案:

答案 0 :(得分:1)

你不能这样做,因为它不安全。如果允许你可以这样做:

class OtherChild : BaseType { }

CallBackHandler clbk2 = callBackB;
clbk2(new OtherChild());

并将OtherChild的实例传递给需要ChildType实例的委托。请注意以下是安全的并且编译:

public delegate void CallBackHandler(ChildType p);

CallBackHandler clbk1 = callBackA;
CallBackHandler clbk2 = callBackB;

答案 1 :(得分:1)

将委托声明为

public delegate void CallBackHandler(ChildType p)

如果处理程序可以接受并且应该对BaseType的实例执行操作,那么如果传入新类NewChildType的实例会发生什么?没人知道,这就是为什么你不能来这里的原因。

答案 2 :(得分:1)

这是经典的类型安全问题,即创建强类型来解决。

这是一个例子

abstract class Vehicle
{
    public virtual void MethodSlot1_StartEngine() { }

    public virtual void MethodSlot2_StopEngine() { }
}

class Car : Vehicle
{
    public virtual void MethodSlot3_OpenGasTank() { }
}

class NuclearSubmarine : Vehicle
{
    public virtual void MethodSlot3_FireAllNuclearMissiles() { }
}

class VehicleUser
{
    public delegate void OpenGasTankMethod(Car car);

    public void OpenGasTank(Vehicle vehicle, OpenGasTankMethod method)
    {
        //it's stopping you here from firing all nuclear weapons
        //by mistaking your car's gas tank for the armageddon switch
        method(vehicle); 
    }
}

当编译器发出虚拟方法调用时,它只是将索引编译到查找表中。如果你只是因为它们都是车辆而你可以通过一个需要汽车的核潜艇,那么你可能会认为你正在调用汽车方法(比如打开你的油箱),而实际上你正在制造辐射系列游戏成为现实。

在回复你的评论时,这应该让你开始:

class Blah
{
    private List<Action<object>> _handlers = new List<Action<object>>();

    public void AddListener<T>(Action<T> handler)
    {
        //graceful type checking code goes in here somewhere
        _handlers.Add(o => handler((T) o));
    }

    void RaiseEvent(object eventArgs)
    {
        foreach (var handler in _handlers) handler(eventArgs);
    }
}