具有泛型参数的类型和动作字典
Dictionary of types and actions with a generic parameter
我正在尝试使用 MessagePack 获得一个简单的消息传递服务。
该服务应保留 dictionary<type, Action<T>>
以便查找如何 "deal" 使用特定消息。
基本上,我有:
private Dictionary<Type, Action<IMessage>> subActions;
public void Subscribe<TMessage>(Action<TMessage> action) where TMessage : IMessage
{
subActions.Add(typeof(TMessage), action);
}
我得到:
Argument 2: cannot convert from 'System.Action<TMessage>' to 'System.Action<GameServer.Messages.IMessage>'
万一不清楚;消息 "arrive" 作为包含 "typecode" 的 byte[]
。从 "typecode" 我得到 Type
,然后 应该 可以查找合适的 Action
来接受。显然,Action
应该将 TMessage
作为参数。
关于编译器为什么给我这个错误,我似乎无法得出合乎逻辑的结论,尽管我怀疑它与 Dictionary
不在与 [= 相同的上下文中有关17=].
提前致谢!
让我们考虑这些方法:
public void MessageMethodInterface(IMessage arg)
{
}
public void MessageMethodClass(MyMessage arg)
{
}
其中 MyMessage
是实现 IMessage
接口的 class。
现在让我们创建一个 Action
并使用它们:
var action = new Action<IMessage>(MessageMethodInterface);
action(new SomeOtherMessage());
SomeOtherMessage
实施 IMessage
。这会起作用。我们有一个 Action
将 IMessage
作为输入。由于 SomeOtherMessage 实现了 IMessage,它可以编译并运行。
现在让我们创建另一个 Action
:
var action2 = new Action<IMessage>(MessageMethodClass);
action(new SomeOtherMessage());
这行不通:我们创建了一个 Action
,它应该接受任何类型 IMessage
的对象,但我们分配给它的方法只能处理类型 MyMessage
的输入] class - 因此我们使用我们提供的方法缩小了操作的范围 - 它不会编译。
您的代码中发生的是场景 2。您希望字典包含可以处理 IMessage
的方法,但您传入的方法只能处理特定类型。这不是类型安全的。即使 class 实现了接口,方法本身也只处理特定类型,而不是该接口的每种类型。与 MyMessage
和 SomeOtherMessage
.
的示例相同
这是Action
类型的逆变。
基本类型的 Action
可以分配派生类型的操作。这是类型安全的 b/c 仅使用基类型的方法将知道如何处理派生类型。反之则不然。以 object
作为输入的方法将处理 string
。由于 string
公开了 object
的所有方法和属性。但反之则不然。
它不起作用,因为你想像这样分配:
Action<IMessage> dest = new Action<TMessage>(x => x.TMessageProp = 123);
所以,如果编译器允许的话,你可以进一步写,例如:
dest(new TAnotherMessage());
因为 TAnotherMessage
也实现了 IMessage
。但它会导致异常,因为 dest
只能与 TMessage
一起使用。阅读有关逆变的更多信息。
您可以保持类型安全和 fix it safely,因为尽管如此您知道类型,您可以通过字典的键将其传递给特定的操作:
public void Subscribe<TMessage>(Action<TMessage> action) where TMessage : IMessage
{
subActions.Add(typeof(TMessage), x => action((TMessage)x));
}
我正在尝试使用 MessagePack 获得一个简单的消息传递服务。
该服务应保留 dictionary<type, Action<T>>
以便查找如何 "deal" 使用特定消息。
基本上,我有:
private Dictionary<Type, Action<IMessage>> subActions;
public void Subscribe<TMessage>(Action<TMessage> action) where TMessage : IMessage
{
subActions.Add(typeof(TMessage), action);
}
我得到:
Argument 2: cannot convert from 'System.Action<TMessage>' to 'System.Action<GameServer.Messages.IMessage>'
万一不清楚;消息 "arrive" 作为包含 "typecode" 的 byte[]
。从 "typecode" 我得到 Type
,然后 应该 可以查找合适的 Action
来接受。显然,Action
应该将 TMessage
作为参数。
关于编译器为什么给我这个错误,我似乎无法得出合乎逻辑的结论,尽管我怀疑它与 Dictionary
不在与 [= 相同的上下文中有关17=].
提前致谢!
让我们考虑这些方法:
public void MessageMethodInterface(IMessage arg)
{
}
public void MessageMethodClass(MyMessage arg)
{
}
其中 MyMessage
是实现 IMessage
接口的 class。
现在让我们创建一个 Action
并使用它们:
var action = new Action<IMessage>(MessageMethodInterface);
action(new SomeOtherMessage());
SomeOtherMessage
实施 IMessage
。这会起作用。我们有一个 Action
将 IMessage
作为输入。由于 SomeOtherMessage 实现了 IMessage,它可以编译并运行。
现在让我们创建另一个 Action
:
var action2 = new Action<IMessage>(MessageMethodClass);
action(new SomeOtherMessage());
这行不通:我们创建了一个 Action
,它应该接受任何类型 IMessage
的对象,但我们分配给它的方法只能处理类型 MyMessage
的输入] class - 因此我们使用我们提供的方法缩小了操作的范围 - 它不会编译。
您的代码中发生的是场景 2。您希望字典包含可以处理 IMessage
的方法,但您传入的方法只能处理特定类型。这不是类型安全的。即使 class 实现了接口,方法本身也只处理特定类型,而不是该接口的每种类型。与 MyMessage
和 SomeOtherMessage
.
这是Action
类型的逆变。
基本类型的 Action
可以分配派生类型的操作。这是类型安全的 b/c 仅使用基类型的方法将知道如何处理派生类型。反之则不然。以 object
作为输入的方法将处理 string
。由于 string
公开了 object
的所有方法和属性。但反之则不然。
它不起作用,因为你想像这样分配:
Action<IMessage> dest = new Action<TMessage>(x => x.TMessageProp = 123);
所以,如果编译器允许的话,你可以进一步写,例如:
dest(new TAnotherMessage());
因为 TAnotherMessage
也实现了 IMessage
。但它会导致异常,因为 dest
只能与 TMessage
一起使用。阅读有关逆变的更多信息。
您可以保持类型安全和 fix it safely,因为尽管如此您知道类型,您可以通过字典的键将其传递给特定的操作:
public void Subscribe<TMessage>(Action<TMessage> action) where TMessage : IMessage
{
subActions.Add(typeof(TMessage), x => action((TMessage)x));
}