Pergunta sobre c#, generics, interface, casting – Interfaces aninhadas: elenco IDictionary <TKey, IList <TValue >> para IDictionary <TKey, IEnumerable <TValue >>?

1

Eu acho que é bastante simples lançar umIDictionary<TKey, IList<TValue>> objeto a umIDictionary<TKey, IEnumerable<TValue>>, mas

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

lança umSystem.InvalidCastExceptione

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

fazval nulo. Qual é a maneira correta de conjurar isso?

Sua resposta

2   a resposta
7

IDictionary<TKey, IList<TValue>> objeto a umIDictionary<TKey, IEnumerable<TValue>>

Absolutamente não. Não seria seguro para o tipo. Aqui está um exemplo do porque não:

<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>

Como você pode ver, isso vai causar problemas - nós estaríamos tentando obterLinkedList<int> quando esperamos que todos os valores implementemIList<int>. O ponto dos genéricos é ser seguro para o tipo - então, qual linha você esperaria falhar? A primeira, terceira e quarta linhas parecem muito claramente válidas para mim - então a segunda é asó um que pode falhar em compilar e faz ...

Agora emalguns casos, pode ser feito com segurança. Por exemplo, você pode converter (em C # 4) deIEnumerable<string> paraIEnumerable<object> PorqueIEnumerable<T> usa apenasT nas posições de "saída".

VejoMSDN para mais detalhes.

EDIT: Só para esclarecer - é fácil criar umNovo dicionário com uma cópia dos pares chave / valor existentes, por ex. usando o link:

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

Você só precisa estar ciente de que agora você tem dois dicionários separados.

@JeremyHolovacs: Veja minha edição - se você está feliz o suficiente para fazer uma cópia, é simples. Mas você não pode "ver" um dicionário de um tipo como um dicionário de ano. Jon Skeet
Eu vejo, porque o objeto do dicionário é de natureza referencial, adicionando o valor abadDictionary adiciona-o adictionary. Como se contornar isso então? Como posso conseguir meuIDictionary<TKey, IEnumerable<TValue>>? Jeremy Holovacs
@JeremyHolovacs: Bem, não é que as coisas sejam adicionadas em dois lugares - é que há apenas um objeto, e os valores de ambas as variáveis ​​se referem ao mesmo objeto. Como ter dois pedaços de papel com o mesmo endereço da casa escrito neles. Você não pode fazer essa conversão - seria simplesmente inseguro fazê-lo. Quero dizer, você poderia escrever sua própria implementação somente leitura deIDictionary<TKey, TValue> que delegou para outro, mas seria feio ... Se você pudesse nos dizer o que estava tentando alcançar (a foto maior), poderíamos ajudá-lo mais. Jon Skeet
Bem, parece que a resposta para essa pergunta é "não é possível". Posso fazer outra pergunta mais tarde sobre como fazer o que quero, mas posso apenas mudar o que quero fazer. Jeremy Holovacs
0

mas pensei em jogá-lo fora como um complemento da resposta de Jon.

Se tudo que você precisa é do dicionáriovalores, sem referência às suas chaves, você pode fazer isso:

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

Para que isso funcione, você deve estar usando o C # 4.0 ou posterior e o TValue deve ser restrito para ser um tipo de referência. Aqui está o código, ligeiramente refatorado e com comentários para explicar:

<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>

Perguntas relacionadas