El título es lo suficientemente básico, ¿por qué no puedo:
Dictionary<string, string> dic = new Dictionary<string, string>();
dic.AddRange(MethodThatReturnAnotherDic());
c#
.net
dictionary
Custodio
fuente
fuente
Respuestas:
Un comentario a la pregunta original lo resume bastante bien:
¿Como qué? Bueno, probablemente porque el comportamiento de combinar diccionarios no se puede razonar de una manera que se ajuste a las pautas del Framework.
AddRange
no existe porque un rango no tiene ningún significado para un contenedor asociativo, ya que el rango de datos permite entradas duplicadas. Por ejemplo, si tiene unaIEnumerable<KeyValuePair<K,T>>
colección que no protege contra entradas duplicadas.El comportamiento de agregar una colección de pares clave-valor, o incluso fusionar dos diccionarios, es sencillo. Sin embargo, el comportamiento de cómo lidiar con múltiples entradas duplicadas no lo es.
¿Cuál debería ser el comportamiento del método cuando se trata de un duplicado?
Hay al menos tres soluciones en las que puedo pensar:
Cuando se lanza una excepción, ¿cuál debería ser el estado del diccionario original?
Add
casi siempre se implementa como una operación atómica: tiene éxito y actualiza el estado de la colección, o falla, y el estado de la colección no cambia. ComoAddRange
puede fallar debido a errores duplicados, la forma de mantener su comportamiento consistente conAdd
sería hacerlo también atómico lanzando una excepción en cualquier duplicado, y dejar el estado del diccionario original sin cambios.Como consumidor de API, sería tedioso tener que eliminar iterativamente elementos duplicados, lo que implica que
AddRange
debería lanzar una única excepción que contenga todos los valores duplicados.La elección luego se reduce a:
Hay argumentos para respaldar ambos casos de uso. Para hacer eso, ¿agregas una
IgnoreDuplicates
bandera a la firma?La
IgnoreDuplicates
bandera (cuando se establece en verdadero) también proporcionaría una aceleración significativa, ya que la implementación subyacente omitiría el código para la verificación duplicada.Así que ahora, tiene una bandera que le permite
AddRange
admitir ambos casos, pero tiene un efecto secundario no documentado (que es algo que los diseñadores de Framework trabajaron muy duro para evitar).Resumen
Como no existe un comportamiento claro, coherente y esperado cuando se trata de tratar con duplicados, es más fácil no tratarlos todos juntos y no proporcionar el método para empezar.
Si tiene que fusionar diccionarios continuamente, por supuesto, puede escribir su propio método de extensión para fusionar diccionarios, que se comportará de una manera que funcione para su (s) aplicación (es).
fuente
AddMultiple
es diferente aAddRange
, independientemente de que su implementación sea inestable: ¿Lanzas una excepción con una matriz de todas las claves duplicadas? ¿O lanza una excepción en la primera clave duplicada que encuentra? ¿Cuál debería ser el estado del diccionario si se lanza una excepción? ¿Prístino, o todas las llaves que tuvieron éxito?Add
, ya sea envolver cada unoAdd
en atry...catch
y capturar los duplicados de esa manera; o use el indexador y sobrescriba el primer valor con el valor posterior; o comprobar de forma preventiva el usoContainsKey
antes de intentarloAdd
, conservando así el valor original. Si el marco tuviera un métodoAddRange
oAddMultiple
, la única forma sencilla de comunicar lo sucedido sería mediante una excepción, y el manejo y la recuperación involucrados no serían menos complicados.Tengo alguna solución:
...
Que te diviertas.
fuente
ToList()
, un diccionario es unIEnumerable<KeyValuePair<TKey,TValue>
. Además, los métodos del segundo y tercer método se lanzarán si agrega un valor de clave existente. No es una buena idea, ¿estabas buscandoTryAdd
? Finalmente, el segundo se puede reemplazar conWhere(pair->!dic.ContainsKey(pair.Key)...
ToList()
no es una buena solución, así que cambié el código. Puede usarlotry { mainDic.AddRange(addDic); } catch { do something }
si no está seguro del tercer método. El segundo método funciona perfectamente.En caso de que alguien se encuentre con esta pregunta como yo, es posible lograr "AddRange" mediante el uso de métodos de extensión IEnumerable:
El principal truco al combinar diccionarios es lidiar con las claves duplicadas. En el código de arriba está la parte
.Select(grp => grp.First())
. En este caso, simplemente toma el primer elemento del grupo de duplicados, pero puede implementar una lógica más sofisticada allí si es necesario.fuente
dict1
no usa el comparador de igualdad predeterminado?var combined = dict1.Concat(dict2).GroupBy(kvp => kvp.Key, dict1.Comparer).ToDictionary(grp => grp.Key, grp=> grp.First(), dict1.Comparer);
Mi conjetura es la falta de una salida adecuada para el usuario sobre lo que sucedió. Como no puede tener claves repetidas en un diccionario, ¿cómo manejaría fusionar dos diccionarios donde algunas claves se cruzan? Seguro que podría decir: "No me importa", pero eso está rompiendo la convención de devolver falso / lanzar una excepción para las claves repetidas.
fuente
Add
, aparte de que puede suceder más de una vez? Tiraría lo mismoArgumentException
queAdd
hace, ¿seguro?NamedElementException
??), ya sea lanzada en lugar de o como la innerException de una ArgumentException, que especifica el elemento con nombre que entra en conflicto ... algunas opciones diferentes, diría yoPodrías hacer esto
o use una Lista para agregar rango y / o usando el patrón anterior.
fuente
MethodThatReturnAnotherDic
. Viene del OP. Por favor revise la pregunta y mi respuesta nuevamente.Si está tratando con un nuevo diccionario (y no tiene filas existentes que perder), siempre puede usar ToDictionary () de otra lista de objetos.
Entonces, en tu caso, harías algo como esto:
fuente
Dictionary<string, string> dic = SomeList.ToDictionary...
Si sabe que no va a tener claves duplicadas, puede hacer lo siguiente:
Lanzará una excepción si hay un par clave / valor duplicado.
No sé por qué esto no está en el marco; debiera ser. No hay incertidumbre; solo lanza una excepción. En el caso de este código, lanza una excepción.
fuente
var caseInsensitiveDictionary = new Dictionary<string, int>( StringComparer.OrdinalIgnoreCase);
?Siéntase libre de usar un método de extensión como este:
fuente
Aquí hay una solución alternativa usando c # 7 ValueTuples (literales de tupla)
Usado como
fuente
Como han mencionado otros, la razón por la
Dictionary<TKey,TVal>.AddRange
que no se implementa es porque hay varias formas en las que puede querer manejar los casos en los que tiene duplicados. Este es también el caso deCollection
o interfaces tales comoIDictionary<TKey,TVal>
,ICollection<T>
, etc.Solo lo
List<T>
implementa, y notará que laIList<T>
interfaz no lo hace, por las mismas razones: el comportamiento esperado al agregar un rango de valores a una colección puede variar ampliamente, dependiendo del contexto.El contexto de su pregunta sugiere que no le preocupan los duplicados, en cuyo caso tiene una alternativa simple de un delineador, usando Linq:
fuente