2009-02-09 23 views
14

鑑於這種神奇的接口:在C#4.0中,爲什麼方法中的out參數不能協變?

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」方法參數爲不變類型。難道這是不允許的嗎?還是在未來的版本中可以解決?

回答

9

有趣。但是,在CLI層面上,不存在「out」這樣的事情 - 只有「ref」;有一個屬性可以幫助編譯器(明確賦值)說「你不需要傳入」。

也許這個限制是因爲CLI沒有「out」,只有「ref」。

+0

有關信息,我發現多個博客等說相同,但沒有評論來自「官方」MS來源。我相當確信它是正確的,但是...... C#4.0差異仍然基於CLI規則。 – 2009-02-09 12:03:08

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)); 
    } 
} 
相關問題