16

この魔法のインターフェースを考えると:

public interface IHat<out TRabbit>
{
    TRabbit Take();
}

そして、このクラス階層:

public class Rabbit { }

public class WhiteRabbit : Rabbit { }

これでコンパイルできます:

IHat<WhiteRabbit> hat1 = null;
IHat<Rabbit> hat2 = hat1;

これは素晴らしいことです。しかし、インターフェースを別の方法で定義するとどうなりますか?

public interface IHat<out TRabbit>
{
    bool Take(out TRabbit r);
}

別のブール値の戻り値を使用して、帽子が空である可能性があることを示しています(以前のバージョンでは、空の帽子からnullのウサギが返された可能性があります)。しかし、私はまだウサギを出力しているだけなので、以前のバージョンと論理的に異なることは何もしていません。

CTPのC#4.0コンパイラでは、インターフェイス定義にエラーが発生します。「out」メソッドパラメータが不変型である必要があります。これが許可されない理由はありますか、それとも将来のバージョンで対処される可能性がありますか?

4

2 に答える 2

9

面白い。ただし、CLIレベルでは、「out」のようなものはなく、「ref」のみです。「それを渡す必要はない」というコンパイラー(明確な割り当て用)を支援する属性があります。

おそらく、この制限は、CLIに「out」がなく「ref」しかないためです。

于 2009-02-09T11:22:21.230 に答える
0

少し面倒ですが、共分散ラッパーを使用できます。

public class CovariantListWrapper<TOut, TIn> : IList<TOut> where TIn : TOut
{
    IList<TIn> list;

    public CovariantListWrapper(IList<TIn> list)
    {
        this.list = list;
    }

    public int IndexOf(TOut item)
    {
        // (not covariant but permitted)
        return item is TIn ? list.IndexOf((TIn)item) : -1;
    }

    public TOut this[int index]
    {
        get { return list[index]; }
        set { throw new InvalidOperationException(); }
    }

    public bool Contains(TOut item)
    {
        // (not covariant but permitted)
        return item is TIn && list.Contains((TIn)item);
    }

    public void CopyTo(TOut[] array, int arrayIndex)
    {
        foreach (TOut t in this)
            array[arrayIndex++] = t;
    }

    public int Count { get { return list.Count; } }

    public bool IsReadOnly { get { return true; } }

    public IEnumerator<TOut> GetEnumerator()
    {
        foreach (TIn t in list)
            yield return t;
    }

    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }

    public void Insert(int index, TOut item) { throw new InvalidOperationException(); }
    public void RemoveAt(int index) { throw new InvalidOperationException(); }
    public void Add(TOut item) { throw new InvalidOperationException(); }
    public void Clear() { throw new InvalidOperationException(); }
    public bool Remove(TOut item) { throw new InvalidOperationException(); }
}

これにより、コレクションを最初に型付けされたとおりに保持し、切り離されたコピーを作成せずに共変的に参照できるため、元の更新が共変使用で見られるようになります。例:

class CovarianceWrapperExample
{
    class Person { }
    class Employee : Person { }

    void ProcessPeople(IList<Person> people) { /* ... */ }

    void Foo()
    {
        List<Employee> employees = new List<Employee>();

        // cannot do:
        ProcessPeople(employees);

        // can do:
        ProcessPeople(new CovariantListWrapper<Person, Employee>(employees));
    }
}
于 2011-02-18T22:27:43.767 に答える