您不能像那样将Func<T, Task> 转换为Func<IMessage, Task>,有几个非常好的理由。但是,您可以 将其转换为Delegate,然后在调用时转换回正确的类型。这在功能上等同于将实例转换为 Object 并再次返回。
这是一个示例:
public class TaskFactory
{
private readonly Dictionary<Type, Delegate> _factories = new Dictionary<Type, Delegate>();
public void Register<T>(Func<T, Task> factory)
{
_factories[typeof(T)] = factory;
}
public Task Invoke<T>(T value)
{
if (_factories.TryGetValue(typeof(T), out var del) && del is Func<T, Task> fn)
return fn.Invoke(value);
return Task.CompletedTask;
}
}
这取决于编译时传递的类型,所以如果你传入IMessage,那么它将尝试为IMessage找到工厂方法,而不是实际的具体类型。
要在运行时处理正确的类型确定,您可以全力以赴并构造正确的类型以进行检查...或者相信您的代码不会搞砸任务工厂缓存的内容。这是值得信赖的版本:
public Task Invoke(object instance)
{
if (_factories.TryGetValue(instance?.GetType(), out var del))
return (Task)del.DynamicInvoke(instance);
return Task.CompletedTask;
}
请注意,这里我们使用DynamicInvoke 运行工厂函数,并将结果转换回Task。只要您的_factories 字典的内容没有发生任何变化,那么您会没事的。诚实。
为了完整起见,这里是偏执的版本。这个找到实例类型,获取工厂方法,然后检查以确保工厂方法与我们期望的类型签名匹配。如果一切顺利那么它将使用DynamicInvoke 来运行函数。
public Task Invoke(object instance)
{
if (instance is null)
throw new ArgumentNullException(nameof(instance));
// Get the true type of the instance
var type = instance.GetType();
if (_factories.TryGetValue(type, out var del))
{
// Get the type for Func<T, Task> where T is the true type of the parameter
var deltype = typeof(Func<,>).MakeGenericType(type, typeof(Task));
// check that the delegate matches our expected delegate type then execute
if (deltype.IsAssignableFrom(del.GetType()))
return (Task)del.DynamicInvoke(instance);
}
return Task.CompletedTask;
}
我在这里基本上忽略了IMessage 接口,但您可以简单地将其插入代码中的适当位置。
关于Subscribe 方法中的类型过滤主题,这似乎有点奇怪。你需要一个实现IMessage的类并且有一个默认的构造函数......但是你永远不会在Subscribe的任何地方构造对象的实例。通常,您只想在代码中放置您实际需要的那些限制,或者放在那里或者以后可能需要这些限制的地方。