在 C# 4.0 中,为啥方法中的 out 参数不能是协变的?

Posted

技术标签:

【中文标题】在 C# 4.0 中,为啥方法中的 out 参数不能是协变的?【英文标题】: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”方法参数为不变类型。是否有明确的理由为什么不允许这样做,或者是否有可能在未来的版本中解决?

【问题讨论】:

【参考方案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));
    

【讨论】:

以上是关于在 C# 4.0 中,为啥方法中的 out 参数不能是协变的?的主要内容,如果未能解决你的问题,请参考以下文章

在 C# 4.0 中是不是应该使用重载或可选参数声明方法?

为啥在面向 .NET Standard 的项目中可以不分配 Out 参数?

C#和Java中的构造器constructor是啥?起啥作用?为啥不可以被override??

为啥 c# 中的 Random 构造函数在方法末尾分配 Seed 参数?

为啥我不能将具有用户定义参数的方法分配给 c# 中的按钮?

方法参数中的 C# 指针?