【问题标题】:In C# 4.0 why can't an out parameter in a method be covariant?在 C# 4.0 中,为什么方法中的 out 参数不能是协变的?
【发布时间】:2010-10-06 09:06:14
【问题描述】:

鉴于这个神奇的界面:

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”方法参数为不变类型。是否有明确的理由为什么不允许这样做,或者是否有可能在未来的版本中解决?

【问题讨论】:

    标签: c# c#-4.0 covariance out


    【解决方案1】:

    有趣。但是,在 CLI 级别没有“out”之类的东西——只有“ref”;有一个属性可以帮助编译器(用于明确赋值),它说“你不需要传递它”。

    也许这个限制是因为 CLI 没有“out”,只有“ref”。

    【讨论】:

    • 对于信息,我发现多个博客等都说相同,但没有一个 cmets 来自“官方”MS 来源。不过,我非常确信它是正确的……C# 4.0 的差异仍然基于 CLI 规则。
    【解决方案2】:

    虽然有点麻烦,但您可以使用协方差包装器:

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

    【讨论】:

      猜你喜欢
      • 2014-12-14
      • 1970-01-01
      • 2012-02-13
      • 2020-12-20
      • 2017-08-03
      • 2016-09-24
      • 2019-01-30
      • 2018-07-03
      相关资源
      最近更新 更多