2012-10-11 88 views
10

我有以下方法在外部類IDictionary <,>逆變?

public static void DoStuffWithAnimals(IDictionary<string, Animal> animals) 

在我的呼喚代碼,我已經有一個Dictionary<string, Lion>對象,但我不能接受這一點,因爲這方法的參數。所以IDictionary<,>不是逆變?我不明白爲什麼這不應該工作。

我能想到的唯一的解決辦法是:

var animals = new Dictionary<string, Animal>(); 

foreach(var kvp in lions) { 
    animals.Add(kvp.Key, kvp.Value); 
} 

有沒有辦法通過這本詞典進入這個方法,而無需創建同一對象的一個​​新的字典?


編輯:

由於這是我的方法,我知道,我是從字典中使用的唯一成員是TValue this[TKey key]的getter,它是IDictionary<TKey, TValue>一員,所以在這種情況下,我無法使用「更寬」類型的參數。

+2

IDictionary的似乎是不變的。其聲明不包含進/出細節。 –

回答

5

解決明智的,你可以做這樣的事情,只是傳遞一個訪問,而不是:

public static void DoStuffWithAnimals(Func<string, Animal> getAnimal) 
    { 
    } 

    var dicLions = new Dictionary<string, Lion>(); 
    DoStuffWithAnimals(s => dicLions[s]); 

顯然,這很可能是一個有點簡單的滿足您的需求,但如果你只需要幾個字典方法,那麼很容易就可以實現。

這是另一種方式,讓您在動物之間的一些代碼重複使用:您可以修改public static void DoStuffWithAnimals(IDictionary<string, Animal> animals)添加通用約束

public class Accessor<T> : IAnimalAccessor where T : Animal 
    { 
     private readonly Dictionary<string, T> _dict; 

     public Accessor(Dictionary<string, T> dict) 
     { 
      _dict = dict; 
     } 

     public Animal GetItem(String key) 
     { 
      return _dict[key]; 
     } 
    } 

    public interface IAnimalAccessor 
    { 
     Animal GetItem(string key); 
    } 

    public static void DoStuffWithAnimals(IAnimalAccessor getAnimal) 
    { 
    } 

    var dicLions = new Dictionary<string, Lion>(); 
    var accessor = new Accessor<Lion>(dicLions); 
    DoStuffWithAnimals(accessor); 
+0

對於我的情況,第一個問題實際上很好,這正是我需要的:) @勞倫斯的答案也適用於我的情況。 – Connell

4

假設DerivedBase的子類型。然後,Dictionary<Base>不能成爲Dictionary<Derived>一個亞型,因爲你不能把Base類型的任何對象爲Dictionary<Derived>,而是因爲如果你得到一個對象了Dictionary<Base>Dictionary<Derived>不能成爲Dictionary<Base>一個亞型,它可能沒有成爲Derived。因此,Dictionary在其類型參數中既不共同也不反轉。

通常,您可以寫入的集合由於這個原因而不變。如果你有一個不變的集合,那麼它可能是協變的。 (如果你有某種只寫收藏,它可能是不可逆轉的。)

編輯:如果你有一個「集合」,你既不能獲取數據也不能將數據放入,那麼它可能是共同的,和逆變。 (這也很可能是無用的。)

+2

例如,'DoStuffWithAnimals'允許爲'動物'添加一個'Puppy',但如果'animals'是一個Lion的集合(除非獅子餓了),那麼這應該是非法的。 – Brian

+0

啊,是的,當然!所以在我的情況下,它是隻讀的,該方法不會添加任何小狗與我的獅子。它唯一會使用的是'TValue this [TKey key]'(它是IDictionary ''的成員)的getter。是否仍然沒有解決方法? – Connell

2

。下面是一些在LINQPad對我的作品:

void Main() 
{ 
    var lions = new Dictionary<string, Lion>(); 
    lions.Add("one", new Lion{Name="Ben"}); 
    AnimalManipulator.DoStuffWithAnimals(lions); 
} 

public class AnimalManipulator 
{ 
    public static void DoStuffWithAnimals<T>(IDictionary<string, T> animals) 
    where T : Animal 
    { 
     foreach (var kvp in animals) 
     { 
      kvp.Value.MakeNoise(); 
     } 
    } 
} 

public class Animal 
{ 
    public string Name {get;set;} 
    public virtual string MakeNoise() 
    { 
     return "?"; 
    } 
} 

public class Lion : Animal 
{ 
    public override string MakeNoise() 
    { 
     return "Roar"; 
    } 
} 
+0

絕妙的主意!感謝那。 +1 – Connell

0

如果字典的界面只讀(允許你只讀取鍵值對,甚至無法允許拉價值的能力它的關鍵),它可以用out通用參數修飾符標記。如果字典的界面是隻寫的(允許您插入值,但不能檢索它們或迭代它們),則可以使用in通用參數修飾符進行標記。

因此,您可以自己創建接口並擴展Dictionary類以獲得所需的功能。

3

首先,C#中的協變和逆變僅適用於接口和委託。

所以你的問題真的是IDictionary<TKey,TValue>

就這樣,最簡單的方法就是記住一個接口只能是co/contra-variant,如果一個類型參數的所有值只傳入或傳出。

例如(方差):

interface IReceiver<in T> // note 'in' modifier 
{ 
    void Add(T item); 
    void Remove(T item); 
} 

和(逆變):

interface IGiver<out T> // note 'out' modifier 
{ 
    T Get(int index); 
    T RemoveAt(int index); 
} 

我永遠記得其中的這些是協變,並且是逆變的,但最終也沒關係,只要你記住這個進/出的區別。

IDictionary<TKey,TValue>的情況下,兩種類型參數都用於輸入和輸出容量,這意味着界面不能是協變或逆變。它是不變的。

但是,類Dictionary<TKey,TValue>確實實施IEnumerable<T>這是協變。

+0

在這種情況下它並不相關,但C#中的共同和逆轉也適用於代表。 – svick

+0

乾杯@svick,我會更新答案。 –

+0

IReadOnlyDictionary可能是逆變呢? – Slugart

2
  1. 我相信你想要的是所謂的協方差而不是反比。
  2. .Net中的類不能共同或逆轉,只有接口和委託可以。
  3. IDictionary<TKey, TValue>不能協變的,因爲這將允許你這樣做:

    IDictionary<string, Sheep> sheep = new Dictionary<string, Sheep>(); 
    IDictionary<string, Animal> animals = sheep; 
    animals.Add("another innocent sheep", new Wolf()); 
    
  4. 有一個在.NET 4.5 IReadOnlyDictionary<TKey, TValue>。乍一看,它可能是協變的(另一個新界面IReadOnlyList<T>是協變的)。不幸的是,它不是,因爲它也實現了IEnumerable<KeyValuePair<TKey, TValue>>KeyValuePair<TKey, TValue>不是協變。

  5. 要解決這個問題,你可以讓你的方法與一般類型參數的約束:

    void DoStuffWithAnimals<T>(IDictionary<string, T> animals) where T : Animal 
    
+0

** 1。**逆向變換從較窄變爲較寬。這不是我想要做的嗎? ** 2。**哎呀。我知道這一點,當我問這個部分時,我已經改變了提到'IDictionary'的問題。 ** 3。**啊哈,是的。這是我從其他答案中學到的。 ** 4。**錯誤。 – Connell

+0

Re 1.不是那麼簡單,我認爲維基百科的解釋令人困惑。 [引用Eric Lippert:](http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx)「考慮一個」操作「操縱類型。如果應用於任何「T」和「U」的操作結果總是產生與「T」和「U」具有相同關係的兩種類型「T」和「U」,則操作被稱爲「協變」。如果手術的結果逆轉了大小,那麼手術就稱爲「逆變」。「 – svick

相關問題