c#通用处理程序,我误解了什么



我不知道为什么这不起作用。它不像TResponse for out和handlermapadd,尽管TResponse是IResponse?我想我一定是误解了泛型,或者更有可能是误解了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>>. tryge·tValue(System. collections . generic . dictionary)。键入,输出System.Collections.Generic.List>)'有一些无效参数C:…NetworkManager.cs 39 13 Assembly -CSharp-vs

错误2参数2:不能从'out System.Collections.Generic '转换。' to 'out System.Collections.Generic。' 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>>?

c#中的差异不允许您将IResponseHandler<IResponse>分配给IResponseHandler<T>,即使T上有where子句。

我不知道你想做什么,因为你没有提供这里使用的所有代码;但是,这将编译:

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

正如其他人所提到的-没有办法做到'你正在做的'…

a)你需要contravariance -为Add工作

b)您需要covariance能够将upcastIResponseHandler<TResponse>转换为IResponseHandler<IResponse>

(你也有另一个编译问题,返回out到不同类型的列表,这两种方式都不能工作)…

对于一个解-如果这个contract满足你的需要,你可以把它trick变成可行的。这更像是一个"练习示例",因为你会失去一些支持——但这取决于你需要什么……

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)你需要outbase interface -使其成为covariant

2)你需要keep it协变-不添加任何东西在它像Add(见评论)。基本上(并且过于简化),您需要维护它read only(请注意这不是真的-只是这样想更容易)。这也适用于参与其中的所有类型/其他参数等。编译器会引导你w/errors

3)从IResponseHandler中提取所有功能到ResponseHandler类-服务器的所有-在那里你可以添加你的Add等-并覆盖特定情况

4)您需要cast才能获得实际可以"处理"的"处理程序"- (handler as ResponseHandler<TResponse>).Handle(response);

注意

…如果你的"处理程序"只是"处理",这完全是futile (Add是你真正需要的唯一方法)-也就是说,这完全取决于你的代码和结构以及实现的东西。如果你的基本接口"服务于"其他目的——那么它可能是值得的。否则——你几乎可以用object做所有这些——并从object进行强制转换,你不会对此感到更少或更高兴。

进一步扩展我的评论,您的IResponseHandler<T>接口在T是逆变 (T出现在"输入"位置)。没有办法做你想做的事情,因为它不是类型安全的。

借用Eric Lippert喜欢用的一个比喻,如果香蕉是一种水果,那么认为一碗香蕉是一碗水果似乎是合理的。然而,只有当你问这个碗里有什么时,这才是类型安全的?如果你想往碗里加东西,那就大错特错了。一碗水果应该能够接受任何水果。然而,如果我们把你的一碗香蕉看成一碗水果,那么你可以在一碗香蕉里放一个橙子,然后弄得一团糟。

编译器阻止你有这种不一致。你的IResponseHandler<T>对象不能接受任何IResponse类型,只能接受特定的IResponse类型。

相关内容

  • 没有找到相关文章

最新更新