簡體   English   中英

在C#4.0中,為什么方法中的out參數不能協變?

[英]In C# 4.0 why can't an out parameter in a method be covariant?

鑒於這個神奇的界面:

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'方法參數為不變類型。 是否有一個強硬的原因,為什么不允許這樣做,或者是否可以在未來版本中解決這個問題?

有趣。 但是,在CLI級別沒有“out”這樣的東西 - 只有“ref”; 有一個屬性可以幫助編譯器(對於明確的賦值)說“你不需要傳入它”。

也許這個限制是因為CLI沒有“out”,只有“ref”。

雖然這有點麻煩,但您可以使用協方差包裝器:

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM