Pytanie w sprawie interface, generics, casting, c# – Zagnieżdżone interfejsy: Cast IDictionary <TKey, IList <TValue>> do IDictionary <TKey, IEnumerable <TValue>>?

1

Sądzę, że rzucanie jest dość prosteIDictionary<TKey, IList<TValue>> sprzeciwić sięIDictionary<TKey, IEnumerable<TValue>>, ale

<code>var val = (IDictionary<TKey, IEnumerable<TValue>>)Value;
</code>

rzuca aSystem.InvalidCastException, i

<code>var val = Value as IDictionary<TKey, IEnumerable<TValue>>;
</code>

sprawiaval zero. Jaki jest właściwy sposób na rzucenie tego?

Twoja odpowiedź

2   odpowiedź
7

IDictionary<TKey, IList<TValue>> sprzeciwić sięIDictionary<TKey, IEnumerable<TValue>>

Absolutnie nie. To nie byłoby bezpieczne dla typu. Oto przykład, dlaczego nie:

<code>// This is fine...
IDictionary<string, IList<int>> dictionary = new Dictionary<string, IList<int>>();

// Suppose this were valid...
IDictionary<string, IEnumerable<int>> badDictionary = dictionary;

// LinkedList<T> doesn't implement IList<T>
badDictionary["foo"] = new LinkedList<int>();

// What should happen now?
IList<int> bang = dictionary["foo"];
</code>

Jak widzisz, będzie to powodować problemy - próbujemy uzyskaćLinkedList<int> kiedy oczekujemy wdrożenia wszystkich wartościIList<int>. Istotą generyków jest to, że mają być bezpieczne dla typu - która linia mogłaby zawieść? Pierwsza, trzecia i czwarta linia wyglądają dla mnie dość wyraźnie - więc drugi totylko taki, który nie może się skompilować, i robi ...

Teraz wtrochę przypadkach, można to zrobić bezpiecznie. Na przykład możesz przekonwertować (w C # 4) zIEnumerable<string> doIEnumerable<object> boIEnumerable<T> używa tylkoT w pozycjach „wyjście”.

WidziećMSDN po więcej szczegółów.

EDYCJA: Aby wyjaśnić - łatwo jest utworzyćNowy słownik z kopią istniejących par klucz / wartość, np. za pomocą linku:

<code>var copy = original.ToDictionary<TKey, IEnumerable<TValue>>(pair => pair.Key,
                                                            pair => pair.Value);
</code>

Musisz tylko mieć świadomość, że masz teraz dwa oddzielne słowniki.

@JeremyHolovacs: Cóż, nie chodzi o to, że rzeczy są dodawane w dwóch miejscach - chodzi o to, że istnieje tylko jeden obiekt, a wartości obu zmiennych odnoszą się do tego samego obiektu. Jak na przykład dwie kartki papieru z tym samym adresem domowym. Nie możesz wykonać tej konwersji - byłoby to po prostu niebezpieczne. To znaczy, możesz napisać własną implementację tylko do odczytuIDictionary<TKey, TValue> które oddelegowały do ​​innego, ale byłoby brzydkie ... Gdybyś mógł nam powiedzieć, co próbowałeś osiągnąć (większy obraz), moglibyśmy być w stanie ci pomóc. Jon Skeet
Cóż, wydaje się, że odpowiedź na to pytanie brzmi „To nie jest możliwe”. Mogę później zadać kolejne pytanie, jak zrobić to, co chcę, ale mogę po prostu zmienić to, co chcę zrobić. Jeremy Holovacs
Takie pytania były tu publikowane co najmniej dziesięć tysięcy razy! Zobacz moją sugestię, która pomogłaby rozbroić porównywalne sytuacje w niektórych przypadkach:Kwerendy typów w sposób ko- lub przeciwwariantowy. Olivier Jacot-Descombes
@JeremyHolovacs: Zobacz moją edycję - jeśli jesteś zadowolony z zrobienia kopii, to proste. Ale nie możesz „przeglądać” słownika jednego typu jako słownika anohtera. Jon Skeet
0

ale pomyślałem, że wyrzucę to jako uzupełnienie odpowiedzi Jona.

Jeśli potrzebujesz tylko słownikawartości, bez odniesienia do ich kluczy, możesz to zrobić:

<code>IDictionary<TKey, IList<TValue>> dictionary = Whatever();
var values = (IEnumerable<IEnumerable<TValue>>)dictionary.Values;
</code>

Aby to działało, musisz używać C # 4.0 lub nowszego, a TValue musi być ograniczony do typu referencyjnego. Oto kod, nieco refaktoryzowany, wraz z komentarzami do wyjaśnienia:

<code>IDictionary<TKey, IList<TValue>> dictionary = Whatever();

//Values returns an ICollection<IList<TValue>>
ICollection<IList<TValue>> temp1 = dictionary.Values;

//ICollection<T> inherits from IEnumerable<T>
IEnumerable<IList<TValue>> temp2 = temp1;

//IEnumerable<T> is covariant
//There is an implicit reference conversion between IList<T> and IEnumerable<T>
//So there is an implicit reference conversion between IEnumerable<IList<T>>
//and IEnumerable<IEnumerable<T>>
IEnumerable<IEnumerable<TValue>> values = temp2;
</code>

Powiązane pytania