O título é básico o suficiente, por que não posso:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
c#
.net
dictionary
Custódio
fonte
fonte
Respostas:
Um comentário à pergunta original resume isso muito bem:
Por quê? Bem, provavelmente porque o comportamento de mesclar dicionários não pode ser fundamentado de uma maneira que se encaixe nas diretrizes do Framework.
AddRange
não existe porque um intervalo não tem nenhum significado para um contêiner associativo, já que o intervalo de dados permite entradas duplicadas. Por exemplo, se você tinha umaIEnumerable<KeyValuePair<K,T>>
coleção, isso não protege contra entradas duplicadas.O comportamento de adicionar uma coleção de pares de valores-chave ou mesmo mesclar dois dicionários é direto. O comportamento de como lidar com várias entradas duplicadas, no entanto, não é.
Qual deve ser o comportamento do método ao lidar com uma duplicata?
Posso pensar em pelo menos três soluções:
Quando uma exceção é lançada, qual deve ser o estado do dicionário original?
Add
quase sempre é implementado como uma operação atômica: é bem-sucedido e atualiza o estado da coleção, ou falha, e o estado da coleção permanece inalterado. ComoAddRange
pode falhar devido a erros duplicados, a maneira de manter seu comportamento consistente comAdd
seria também torná-lo atômico, lançando uma exceção em qualquer duplicata, e deixar o estado do dicionário original inalterado.Como um consumidor de API, seria tedioso ter que remover iterativamente os elementos duplicados, o que implica que o
AddRange
deve lançar uma única exceção que contém todos os valores duplicados.A escolha então se resume a:
Existem argumentos para apoiar os dois casos de uso. Para fazer isso, você adiciona um
IgnoreDuplicates
sinalizador à assinatura?O
IgnoreDuplicates
sinalizador (quando definido como verdadeiro) também forneceria uma velocidade significativa, pois a implementação subjacente ignoraria o código para verificação duplicada.Agora, você tem um sinalizador que permite que o
AddRange
suporte ambos os casos, mas tem um efeito colateral não documentado (que é algo que os designers do Framework trabalharam muito para evitar).Resumo
Como não há um comportamento claro, consistente e esperado quando se trata de lidar com duplicatas, é mais fácil não lidar com todas elas juntas e não fornecer o método para começar.
Se você está continuamente tendo que mesclar dicionários, pode, é claro, escrever seu próprio método de extensão para mesclar dicionários, que se comportará de uma maneira que funcione para seu (s) aplicativo (s).
fonte
AddMultiple
é diferente deAddRange
, independentemente de sua implementação, será difícil: você lança uma exceção com um array de todas as chaves duplicadas? Ou você lança uma exceção na primeira chave duplicada que encontrar? Qual deve ser o estado do dicionário se uma exceção for lançada? Pristine, ou todas as chaves que funcionaram?Add
- embrulhar cadaAdd
um em umtry...catch
e capturar as duplicatas dessa forma; ou use o indexador e sobrescreva o primeiro valor pelo valor posterior; ou verifique preventivamente usandoContainsKey
antes de tentarAdd
, preservando assim o valor original. Se a estrutura tivesse um métodoAddRange
ouAddMultiple
, a única maneira simples de comunicar o que aconteceu seria por meio de uma exceção, e o manuseio e a recuperação envolvidos não seriam menos complicados.Eu tenho alguma solução:
...
Diverta-se.
fonte
ToList()
, um dicionário é umIEnumerable<KeyValuePair<TKey,TValue>
. Além disso, o segundo e o terceiro métodos serão lançados se você adicionar um valor de chave existente. Não é uma boa ideia, você estava procurandoTryAdd
? Finalmente, o segundo pode ser substituído porWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()
não é uma boa solução, então mudei o código. Você pode usartry { mainDic.AddRange(addDic); } catch { do something }
se não tiver certeza para o terceiro método. O segundo método funciona perfeitamente.Caso alguém se depare com esta questão como eu - é possível obter "AddRange" usando os métodos de extensão IEnumerable:
O principal truque ao combinar dicionários é lidar com as chaves duplicadas. No código acima é a parte
.Select(grp => grp.First())
. Nesse caso, ele simplesmente pega o primeiro elemento do grupo de duplicatas, mas você pode implementar uma lógica mais sofisticada lá, se necessário.fonte
dict1
não usar o comparador de igualdade padrão?var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
Meu palpite é a falta de saída adequada para o usuário quanto ao que aconteceu. Como você não pode ter chaves repetidas em um dicionário, como você lidaria com a fusão de dois dicionários onde algumas chaves se cruzam? Claro, você poderia dizer: "Não me importo", mas isso está quebrando a convenção de retornar false / lançar uma exceção para teclas repetidas.
fonte
Add
, além de que pode acontecer mais de uma vez. Seria o mesmoArgumentException
que jogaAdd
, certo?NamedElementException
??), lançado em vez de ou como a innerException de um ArgumentException, que especifica o elemento nomeado em conflito ... algumas opções diferentes, eu diriaVocê poderia fazer isso
ou use uma lista para adicionar intervalo e / ou usando o padrão acima.
fonte
MethodThatReturnAnotherDic
. Vem do OP. Reveja a pergunta e a minha resposta novamente.Se você estiver lidando com um novo Dicionário (e não tiver linhas existentes para perder), você sempre pode usar ToDictionary () de outra lista de objetos.
Então, no seu caso, você faria algo assim:
fonte
Dictionary<string, string> dic = SomeList.ToDictionary...
Se você sabe que não terá chaves duplicadas, pode fazer:
Ele lançará uma exceção se houver um par de chave / valor duplicado.
Não sei por que isso não está na estrutura; deveria estar. Não há incerteza; apenas lance uma exceção. No caso desse código, ele lança uma exceção.
fonte
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);
?Sinta-se à vontade para usar o método de extensão como este:
fonte
Aqui está uma solução alternativa usando c # 7 ValueTuples (literais de tupla)
Usado como
fonte
Como outros mencionaram, o motivo pelo qual
Dictionary<TKey,TVal>.AddRange
não foi implementado é porque existem várias maneiras de lidar com casos em que há duplicatas. Este é também o caso paraCollection
ou interfaces, tais comoIDictionary<TKey,TVal>
,ICollection<T>
, etc.Apenas o
List<T>
implementa, e você notará que aIList<T>
interface não, pelos mesmos motivos: o esperado comportamento ao adicionar um intervalo de valores a uma coleção pode variar amplamente, dependendo do contexto.O contexto de sua pergunta sugere que você não está preocupado com duplicatas e, nesse caso, você tem uma alternativa simples de uma linha, usando o Linq:
fonte