2

分散に関していくつかの問題がありますが、完全には理解していません。次のような2つの型パラメーターを持つジェネリックインターフェイスがあります。

public interface IInvoker<TParameter, TResult> {
    TResult Invoke(TParameter parameter);
}

さて、私の場合は、次のような抽象クラスにしたいと思いますTATB

public abstract class AbstractParameter {
    public int A { get; set; }
}
public abstract class AbstractResult {
    public string X { get; set; }
}

public class Parameter1 : AbstractParameter {
    public int B { get; set; }
}
public class Result1 : AbstractResult {
    public string Y { get; set; }
}
// ... Many more types

次に、のさまざまな実装のセットを処理したいIInvoker<,>ので、このようなことができると思いました

public class InvokerOne : IInvoker<Parameter1, Result1> { /* ... */ }
public class InvokerTwo : IInvoker<Parameter2, Result2> { /* ... */ }

// ..
IInvoker<AbstractParameter, AbstractResult>[] invokers = { new InvokerOne(), new InvokerTwo() };

私が理解している限り、これは(および友人IInvoker<AbstractParameter, AbstractResult>から)割り当てることができないため、機能しません。最初に、これがインターフェイス( )IInvoker<Parameter1, Result1>を叩くときだと思いましたがin、それは役に立ちませんでした。outinterface IInvoker<in TParameter, out TResult>

でもなぜか分かりませんか?私の知る限り、を使用している人は誰でもIInvoker<AbstractParameter, AbstractResult>電話をかけることができるはずInvokeですよね?私は何が欠けていますか?

4

2 に答える 2

5

問題は、TResultタイプパラメータが逆バリアントであるということですが、割り当てでそれらを共変的に使用しようとしています。

IInvoker<AbstractParameter, AbstractResult> i1 = new InvokerOne();

TResultは共変であるため、。AbstractResultよりも大きいタイプでもかまいませんResult1。ただし、TParameterは逆変でTParameterあるため、よりも小さいタイプである必要があります。Parameter1これは、の場合には当てはまりませんAbstractParameter

上記が有効である場合は、次のことができます。

class OtherParameter : AbstractParameter { ... };
IInvoker<AbstractParameter, AbstractResult> i1 = new InvokerOne();
i1.Invoke(new OtherParameter());

これは安全ではありません。

ただし、次のようにすることができます。

public class OtherParameter1 : Parameter1 { }
IInvoker<OtherParameter1, AbstractResult> i1 = new InvokerOne();

ここは常に有効であるためOtherParameter1、パラメータとしてに渡すことができます。InvokeParameter1

于 2013-01-14T23:16:08.720 に答える
1

欠落していることの1つは、インターフェースの差異宣言です。次のように宣言しない限り、インターフェースはバリアントではありません。

public interface IInvoker<in TParameter, out TResult>
//                        ^^             ^^^
//                        Look!          Here too!
{
    TResult Invoke(TParameter parameter);
}

inandoutキーワードは、差異の性質を強調するのに役立ちます。タイプは、パラメーターに関しては反変であり、inパラメーターに関しては共変outです。つまり、通常のAnimal : Mammal : Cat例を想定して、これを行うことができます。

IInvoker<Mammal, Mammal> a = Whatever();
IInvoker<Cat, Mammal> b = a;
IInvoker<Mammal, Animal> c = a;

それ自体は特に便利ではありませんが、要点は、またはIInvoker<Mammal, Mammal>が必要な場所ならどこでも使用できるということです。IInvoker<Cat, Mammal>IInvoker<Mammal, Animal>

IInvoker<,>また、あなたの質問には重要な欠落があります。実装のセットで正確に何をしたいですか?(「...のさまざまな実装のセットを処理したいIInvoker<,>。」)この質問への回答は、あなたの解決策につながります。AbstractParameterから継承するいくつかのオブジェクトを使用して、それらすべてを呼び出しますか?もしそうなら、リーが説明するように、あなたが望むことをすることができれば、これを妨げるものは何もないので、あなたはいくつかの問題を抱えているでしょう:

IInvoker<AbstractParameter, AbstractResult>[] invokers = { new InvokerOne(), new InvokerTwo() };
AbstractParameter[] parameters = { new ParameterOne(), new ParameterTwo() };
AbstractResult[] results = { invokers[0].Invoke(parameters[1] /* oops */), invokers[1].Invoke(parameters[0] /* oops */) };

この問題を解決する1つの方法は、インターフェイスからパラメータを削除することです。これを呼び出し元のプライベートフィールドにするか、呼び出し元とパラメーターをペアにするクラスを作成します。たとえば、次のようになります。

interface IInvokerParameterPair<out TResult>()
    where TResult : AbstractResult
{
    TResult InvokeTheInvoker();
}

class InvokerParameterPair<TParameter, TResult> : IInvokerParameterPair<TResult>
    where TParameter : AbstractParameter 
    where TResult : AbstractResult
{
    private IInvoker<TParameter, TResult> _invoker;
    private TParameter _parameter;
    public InvokerParameterPair(IInvoker<TParameter, TResult> invoker, TParameter parameter)
    {
        _invoker = invoker;
        _parameter = parameter;
    }
    public TResult InvokeTheInvoker()
    {
        return _invoker.Invoke(_parameter);
    }
}

一方、Invokeメソッドとは関係のない処理を実行する場合、呼び出し元は他の共通インターフェースを実装するか、次のような他の共通基本クラスから継承する必要があります。

public interface IProcessable { }
public interface IInvoker<in TParameter, out TResult> : IProcessable
{
    TResult Invoke(TParameter parameter);
}

public class InvokerOne : IInvoker<Parameter1, Result1> { /* ... */ }
public class InvokerTwo : IInvoker<Parameter2, Result2> { /* ... */ }

IProcessable[] invokers = { new InvokerOne(), new InvokerTwo() };

またはこれ:

public interface IInvoker<in TParameter, out TResult> : IProcessable
{
    TResult Invoke(TParameter parameter);
}

public abstract class Processable { }
public class InvokerOne : Processable, IInvoker<Parameter1, Result1> { /* ... */ }
public class InvokerTwo : Processable, IInvoker<Parameter2, Result2> { /* ... */ }

Processable[] invokers = { new InvokerOne(), new InvokerTwo() };
于 2013-01-15T00:44:00.080 に答える