Diccionario que devuelve un valor predeterminado si la clave no existe

Me encuentro utilizando el patrón actual con bastante frecuencia en mi código hoy en día

var dictionary = new Dictionary<type, IList>(); // Add stuff to dictionary var somethingElse = dictionary.ContainsKey(key) ? dictionary[key] : new List(); // Do work with the somethingelse variable 

O algunas veces

 var dictionary = new Dictionary<type, IList>(); // Add stuff to dictionary IList somethingElse; if(!dictionary.TryGetValue(key, out somethingElse) { somethingElse = new List(); } 

Ambas formas se sienten bastante indirectas. Lo que realmente me gustaría es algo como

 dictionary.GetValueOrDefault(key) 

Ahora, podría escribir un método de extensión para la clase de diccionario que hace esto por mí, pero pensé que podría estar perdiendo algo que ya existe. Entonces, ¿hay alguna forma de hacer esto de una manera que sea más “fácil de entender” sin escribir un método de extensión en el diccionario?

TryGetValue ya asignará el valor predeterminado para el tipo al diccionario, por lo que puede usar:

 dictionary.TryGetValue(key, out value); 

y simplemente ignore el valor de retorno. Sin embargo, eso realmente solo devolverá el default(TValue) , no algún valor predeterminado personalizado (ni, más útilmente, el resultado de ejecutar un delegado). No hay nada más poderoso incorporado en el marco. Sugeriría dos métodos de extensión:

 public static TValue GetValueOrDefault (this IDictionary dictionary, TKey key, TValue defaultValue) { TValue value; return dictionary.TryGetValue(key, out value) ? value : defaultValue; } public static TValue GetValueOrDefault (this IDictionary dictionary, TKey key, Func defaultValueProvider) { TValue value; return dictionary.TryGetValue(key, out value) ? value : defaultValueProvider(); } 

(Es posible que desee poner la comprobación de los argumentos, por supuesto 🙂

Sé que esta es una publicación anterior y sí estoy a favor de los métodos de extensión, pero esta es una clase simple que uso de vez en cuando para manejar diccionarios cuando necesito valores predeterminados.

Desearía que esto fuera parte de la clase básica del diccionario.

 public class DictionaryWithDefault : Dictionary { TValue _default; public TValue DefaultValue { get { return _default; } set { _default = value; } } public DictionaryWithDefault() : base() { } public DictionaryWithDefault(TValue defaultValue) : base() { _default = defaultValue; } public new TValue this[TKey key] { get { TValue t; return base.TryGetValue(key, out t) ? t : _default; } set { base[key] = value; } } } 

Ten cuidado, sin embargo. Al subclasar y usar new (ya que override no está disponible en el tipo de Dictionary nativo), si un objeto DictionaryWithDefault se reubica en un Dictionary simple, llamar al indexador utilizará la implementación del Dictionary base (lanzando una excepción si falta) en lugar de la implementación de la subclase .

¡ Creé un DefaultableDictionary para hacer exactamente lo que estás pidiendo!

 using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; namespace DefaultableDictionary { public class DefaultableDictionary : IDictionary { private readonly IDictionary dictionary; private readonly TValue defaultValue; public DefaultableDictionary(IDictionary dictionary, TValue defaultValue) { this.dictionary = dictionary; this.defaultValue = defaultValue; } public IEnumerator> GetEnumerator() { return dictionary.GetEnumerator(); } IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } public void Add(KeyValuePair item) { dictionary.Add(item); } public void Clear() { dictionary.Clear(); } public bool Contains(KeyValuePair item) { return dictionary.Contains(item); } public void CopyTo(KeyValuePair[] array, int arrayIndex) { dictionary.CopyTo(array, arrayIndex); } public bool Remove(KeyValuePair item) { return dictionary.Remove(item); } public int Count { get { return dictionary.Count; } } public bool IsReadOnly { get { return dictionary.IsReadOnly; } } public bool ContainsKey(TKey key) { return dictionary.ContainsKey(key); } public void Add(TKey key, TValue value) { dictionary.Add(key, value); } public bool Remove(TKey key) { return dictionary.Remove(key); } public bool TryGetValue(TKey key, out TValue value) { if (!dictionary.TryGetValue(key, out value)) { value = defaultValue; } return true; } public TValue this[TKey key] { get { try { return dictionary[key]; } catch (KeyNotFoundException) { return defaultValue; } } set { dictionary[key] = value; } } public ICollection Keys { get { return dictionary.Keys; } } public ICollection Values { get { var values = new List(dictionary.Values) { defaultValue }; return values; } } } public static class DefaultableDictionaryExtensions { public static IDictionary WithDefaultValue(this IDictionary dictionary, TValue defaultValue ) { return new DefaultableDictionary(dictionary, defaultValue); } } } 

Este proyecto es un simple decorador para un objeto IDictionary y un método de extensión para que sea fácil de usar.

DefaultableDictionary permitirá crear un contenedor alrededor de un diccionario que proporciona un valor predeterminado al intentar acceder a una clave que no existe o enumerar a través de todos los valores en un IDictionary.

Ejemplo: var dictionary = new Dictionary().WithDefaultValue(5);

Publicación del blog sobre el uso también.

No, nada de eso existe. El método de extensión es el camino a seguir, y su nombre ( GetValueOrDefault ) es una muy buena opción.