无法从 'method group' 转换为 Action

Cannot convert from 'method group' to Action

先post代码会更容易,然后问我为什么会收到这个错误。

摘要class - 数据包

abstract class Packet
{
    // base class!
}

我的第一个包裹

public sealed class FirstPacket : Packet
{
    // First packet implementations...
}

另一个数据包

public sealed class AnotherPacket : Packet
{
    // Another packet implementations...
}

数据包操作码

public enum OpCode
{
    FirstPacket,
    AnotherPacket
}

摘要class - BaseConnection

public abstract class BaseConnection
{
    private Dictionary<OpCode, Action<Packet>> _packetHandlers;

    public Connection() {
        _packetHandlers = new Dictionary<OpCode, Action<Packet>>();
    }
}

最后,我的客户

public sealed class Client : BaseConnection
{
    public Client() : base() {
        // Here will throw the errors...
        // CS1503   Argument 2: cannot convert from 'method group' to 'Action<Packet>'
        _packetHandlers.Add(OpCode.FirstPacket, OnReceiveFirst);
        _packetHandlers.Add(OpCode.AnotherPacket, OnReceiveAnother);
    }

    public void OnReceiveFirst(FirstPacket packet) {
    }

    public void OnReceiveAnother(AnotherPacket packet) {
    }
}

根据 this 的回答,派生的 class 是其基础 class 的一个实例,不涉及转换。

在我的代码中,如果 FirstPacketAnotherPacket Packet,为什么我必须 "cast" 使用 lambda ?

public sealed class Client : BaseConnection
{
    public Client() : base() {
        // This works...
        _packetHandlers.Add(OpCode.FirstPacket, p => { OnReceiveFirst((FirstPacket)p); });
        _packetHandlers.Add(OpCode.AnotherPacket, p => { OnReceiveAnother((AnotherPacket)p); });
    }

    public void OnReceiveFirst(FirstPacket packet) {
    }

    public void OnReceiveAnother(AnotherPacket packet) {
    }
}

这对我来说没有意义。

因为 onReceiveFirstOnReceiveAnother 期望比 Packet 更具体的类型。 FirstPacketAnotherPacket都是包的类型,但是AnotherPacket不能代替FirstPacket,反之亦然。

如果 OnReceiveFirstOnReceiveAnother 只需要访问由 Packet 类型声明的东西而不是子类中的任何东西,您可以将定义更改为:

public void OnReceiveFirst(Packet packet) {}
public void OnReceiveAnother(Packet packet) {}

首先,请注意您的 lambdas

p => { OnReceiveFirst((FirstPacket)p); }

没有转换将无法编译。

之所以可以进行强制转换,是因为您对系统的逻辑了解得足够多,可以决定永远不会使用 SecondPacket 的参数调用 OnReceiveFirst。因此,您得出结论,演员表是安全的。

另一方面,编译器无法得出相同的结论,因此它会要求您手动提供转换。

方法组为不需要强制转换的情况提供了一种快捷方式。例如,如果您像这样重写 OnReceiveFirst

public void OnReceiveFirst(Packet packetOrig) {
    FirstPacket packet = (FirstPacket)packetOrig;
    ...
}

您可以将其与方法组语法一起使用:

_packetHandlers.Add(OpCode.FirstPacket, OnReceiveFirst); // Compiles

在这里,转换仍然是你的责任,因为如果转换抛出异常,你将能够将错误追溯到你自己的代码,而不是某些编译器魔法。