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);
}

我表示帽子可能是空的,使用单独的布尔返回值(以前的版本可能会从空帽子返回空兔子)。但我仍然只输出一只兔子,所以没有做任何与以前版本在逻辑上不同的事情。

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 回答