2

なぜこれが機能しないのかわかりません。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>>.TryGe‌ tValue(System.Type, out System.Collections.Generic.List>)' に一致する最適なオーバーロード メソッドには、無効な引数 C:...\NetworkManager があります。 cs 39 13 アセンブリー-CSharp-vs

エラー 2 引数 2: 'out System.Collections.Generic.List>' から 'out System.Collections.Generic.List>' 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>>?
4

3 に答える 3

1

C# の差異により、に where 句がある場合でも、IResponseHandler<IResponse>をに割り当てることができません。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

于 2013-03-30T21:10:29.433 に答える
1

他の人が言ったように-「あなたがやっている方法」でそれを行う方法はありません...

a) が必要ですcontravariance- がAdd動作するために

b)からまでcovarianceできる必要がありますupcastIResponseHandler<TResponse>IResponseHandler<IResponse>

out(また、どちらの方法でも機能しない別のタイプのリストに 戻ると、別のコンパイルの問題があります)...

解決策として-これが必要なものを満たしている場合は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);

興味のあることがいくつかありますが、すぐに...

out1) 作るにはbase interface-が必要ですcovariant

2)共変する必要がありますkeep it-その中に何も追加しないでAddください(コメントを参照)。基本的に(そして過度に単純化された)それを維持する必要がありますread only(これは真実ではないことに注意してください-そのように考える方が簡単です)。また、それに参加するすべてのタイプ/その他のパラメーターなどにも当てはまります。コンパイラがエラーをガイドします

IResponseHandler3)すべての機能をクラスに引き出しResponseHandlerます-そのサーバーすべて-そこにあなたAddなどを追加できます-特定のケースでオーバーライドします

cast4)実際に「処理」できる「ハンドラー」に到達する必要があります-それ(handler as ResponseHandler<TResponse>).Handle(response);

ノート

...これは完全にfutile、「ハンドラー」が「処理」のみの場合 (そしてそれAddが本当に必要な唯一のメソッド) です。つまり、これはコードと構造、および実装に完全に依存します。基本インターフェースがそれ以外の目的で「目的を果たす」場合、それは価値があるかもしれません。それ以外の場合は、ほぼすべてのことを行うことができobject、キャストからキャストしてobjectも、それほど満足することはありません。

于 2013-03-30T21:43:14.650 に答える
0

私のコメントをさらに拡張すると、あなたのIResponseHandler<T>インターフェースは反変ですTT「入力」位置に表示されます)。タイプセーフではないため、あなたが望むことをする方法はありません。

Eric Lippert が好んで使う類推を盗むために、もしバナナが果物なら、バナナのボウルは果物のボウルであると考えるのが合理的に思えるかもしれません. ただし、これは、このボウルに何が入っているかを尋ねる場合にのみタイプ セーフです。ボウルに追加しようとすると、すべてが間違っています。果物のボウルは、どんな果物でも受け入れることができるはずです。ただし、バナナのボウルをフルーツのボウルと見なすことができる場合、バナナのボウルにオレンジを追加すると、混乱する可能性があります.

コンパイラは、その不一致を防ぐことができません。オブジェクトは、特定のタイプのみIResponseHandler<T>を受け入れることができません。IResponseIResponse

于 2013-03-30T21:20:24.880 に答える