我不确定为什么这不起作用。即使TResponse是IResponse,它也不喜欢out和handlerMap添加的TResponse?我想我必须误解一些关于泛型,或者更可能是关于C#的东西。为什么这不起作用,是否有更好的方法来完成我在这里尝试做的事情?
private static Dictionary<Type, List<IResponseHandler<IResponse>>> _handlerMap;
public static void AddResponseHandler<TResponse>(IResponseHandler<TResponse> handler) where TResponse : IResponse
{
List<IResponseHandler<TResponse>> handlers;
_handlerMap.TryGetValue(typeof (TResponse), out handlers);
if (handlers == null)
{
handlers = new List<IResponseHandler<TResponse>>();
_handlerMap.Add(typeof (TResponse), handlers);
}
handlers.Add(handler);
}
public interface IResponseHandler<TResponse> where TResponse : IResponse
{
void Handle(TResponse response);
}
我在编译期间遇到这些错误:
错误1'System.Collections.Generic.Dictionary&gt;&gt; .TryGe tValue(System.Type,out System.Collections.Generic.List&gt;)'的最佳重载方法匹配有一些无效的参数C:.. 。\ NetworkManager.cs 39 13汇编-CSharp-vs
错误2参数2:无法从'out System.Collections.Generic.List&gt;'转换'out System.Collections.Generic.List&gt;' C:... \ NetworkManager.cs 39 61 Assembly-CSharp-vs
If I change TResponse to IResponse within the method, everything above
handlers.Add(handler) compiles fine. I don't understand why I can't add a handler of
<TResponse : IResponse> to a List<IResponseHandler<IReponse>>?
答案 0 :(得分:1)
即使IResponseHandler<IResponse>
上有where子句,C#中的差异也不允许您将IResponseHandler<T>
分配给T
。
我不知道你要做什么,因为你没有提供所有在这里使用的代码;但是,这将编译:
public class SomeClass<TResponse> where TResponse : IResponse
{
private static Dictionary<Type, List<IResponseHandler<TResponse>>> _handlerMap;
public static void AddResponseHandler(IResponseHandler<TResponse> handler)
{
List<IResponseHandler<TResponse>> handlers;
_handlerMap.TryGetValue(typeof(TResponse), out handlers);
if (handlers == null)
{
handlers = new List<IResponseHandler<TResponse>>();
_handlerMap.Add(typeof(TResponse), handlers);
}
handlers.Add(handler);
}
}
这会将泛型从方法移动到类,因此您可以定义兼容的_handlerMap
。
答案 1 :(得分:1)
正如其他人所说的那样 - 没有办法做到这一点`就像你做的那样'......
a)您需要contravariance
- Add
才能工作
b)您需要covariance
才能upcast
从IResponseHandler<TResponse>
到IResponseHandler<IResponse>
(还有另一个编译问题,将out
返回到不同类型的List中,这两种方法无法正常工作)...
对于解决方案 - 如果trick
满足您的需求,您可以contract
使其成为工作类型。这更像是一个“实践范例”,因为你失去了一些支持 - 但取决于你需要什么......
interface IResponse { }
interface IResponseHandler<out TResponse>
where TResponse : class, IResponse
{
// add 'read-only' (simplified) properties only - that support 'covariance' - meaning no 'input parameters' of T etc.
// void Handle(TResponse response);
}
abstract class ResponseHandler<TResponse> : IResponseHandler<TResponse>
where TResponse : class, IResponse
{
public abstract void Handle(TResponse response);
}
class TestHandler
{
private static Dictionary<Type, List<IResponseHandler<IResponse>>> _handlerMap = new Dictionary<Type,List<IResponseHandler<IResponse>>>();
public static void AddResponseHandler<TResponse>(IResponseHandler<TResponse> handler) where TResponse : class, IResponse
{
List<IResponseHandler<IResponse>> handlers;
_handlerMap.TryGetValue(typeof(TResponse), out handlers);
if (handlers == null)
{
handlers = new List<IResponseHandler<IResponse>>();
_handlerMap.Add(typeof(TResponse), handlers);
}
IResponseHandler<IResponse> myhandler = handler;
handlers.Add(myhandler);
}
public static void Handle<TResponse>(TResponse response) where TResponse : class, IResponse
{
List<IResponseHandler<IResponse>> handlers;
_handlerMap.TryGetValue(typeof(TResponse), out handlers);
if (handlers == null) return;
foreach (var handler in handlers)
{
(handler as ResponseHandler<TResponse>).Handle(response);
}
}
}
// and implementation...
class FirstResponse : IResponse { }
class AutomatedResponse : IResponse { }
class FirstHandler : ResponseHandler<FirstResponse>
{
public override void Handle(FirstResponse response) { }
}
class AutomatedHandler : ResponseHandler<AutomatedResponse>
{
public override void Handle(AutomatedResponse response) { }
}
// ...and a test...
var firsthandler = new FirstHandler();
var secondhandler = new AutomatedHandler();
TestHandler.AddResponseHandler(firsthandler);
TestHandler.AddResponseHandler(secondhandler);
var first = new FirstResponse();
var second = new AutomatedResponse();
TestHandler.Handle(first);
TestHandler.Handle(second);
有几件有趣的事情,很快......
1)out
上需要base interface
- 才能covariant
2)您需要keep it
协变 - 不要在Add
中添加任何内容(请参阅注释)。基本上(并且过度简化)你需要维护它read only
(标记这不是真的 - 只是更容易这样思考)。这也适用于参与其中的所有类型/其他参数等。编译器将指导您错误
3)将所有功能从IResponseHandler
拉出到ResponseHandler
类 - 该服务器全部 - 您可以添加Add
等等 - 并覆盖特定情况
4)你需要cast
才能进入实际上可以“处理”的“处理程序” - (handler as ResponseHandler<TResponse>).Handle(response);
...如果你的'处理程序'只是'处理'(futile
是你真正需要的唯一方法),这完全是Add
- 这完全取决于你的代码和结构和事物的实施。如果您的基本界面“用于其他目的” - 那么它可能是值得的。否则 - 您可以使用object
完成所有这些操作 - 并从object
投射,您将不会更喜欢它。
答案 2 :(得分:0)
进一步扩展我的评论,您的IResponseHandler<T>
界面在<{1}}上是逆变(T
出现在“输入”位置)。没有办法做你想要的,因为它不是类型安全的。
要窃取Eric Lippert喜欢使用的类比,如果香蕉是水果,那么认为一碗香蕉是一碗水果似乎是合理的。但是,如果你问这个碗里有什么东西,这只是类型安全的吗?如果你尝试添加到碗里,那就错了。一碗水果应该能够接受任何水果。但是,如果我们可以将你的一碗香蕉视为一碗水果,那么你可以在一碗香蕉中加入橙子并弄得一团糟。
编译器阻止您出现这种不一致。您的T
个对象无法接受任何IResponseHandler<T>
,只能接受特定的IResponse
类型。