wake-up-neo.com

Wörterbuch, das einen Standardwert zurückgibt, wenn der Schlüssel nicht vorhanden ist

Heutzutage verwende ich häufig das aktuelle Muster in meinem Code

var dictionary = new Dictionary<type, IList<othertype>>();
// Add stuff to dictionary

var somethingElse = dictionary.ContainsKey(key) ? dictionary[key] : new List<othertype>();
// Do work with the somethingelse variable

Oder manchmal

var dictionary = new Dictionary<type, IList<othertype>>();
// Add stuff to dictionary

IList<othertype> somethingElse;
if(!dictionary.TryGetValue(key, out somethingElse) {
    somethingElse = new List<othertype>();
}

Beide Wege fühlen sich ziemlich rund an. Was ich wirklich möchte, ist so etwas wie

dictionary.GetValueOrDefault(key)

Jetzt könnte ich eine Erweiterungsmethode für die Dictionary-Klasse schreiben, die dies für mich erledigt, aber ich dachte, dass mir möglicherweise etwas fehlt, das bereits vorhanden ist. Also, gibt es eine Möglichkeit, dies auf eine Weise zu tun, die "augenschonender" ist, ohne eine Erweiterungsmethode für das Wörterbuch zu schreiben?

209
wasatz

TryGetValue weist dem Wörterbuch bereits den Standardwert für den Typ zu, sodass Sie einfach Folgendes verwenden können:

dictionary.TryGetValue(key, out value);

und ignorieren Sie einfach den Rückgabewert. Dies ist jedoch wirklich wird nur default(TValue) zurückgeben, nicht irgendein benutzerdefinierter Standardwert (noch nützlicher das Ergebnis der Ausführung eines Delegaten). In das Framework ist nichts Mächtigeres eingebaut. Ich würde zwei Erweiterungsmethoden vorschlagen:

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

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

(Vielleicht möchten Sie natürlich Argumente einchecken :)

269
Jon Skeet

Ich weiß, dass dies ein alter Beitrag ist und ich Erweiterungsmethoden bevorzuge, aber hier ist eine einfache Klasse, die ich von Zeit zu Zeit verwende, um Wörterbücher zu behandeln, wenn ich Standardwerte benötige.

Ich wünschte, dies wäre nur ein Teil der Basis-Dictionary-Klasse.

public class DictionaryWithDefault<TKey, TValue> : Dictionary<TKey, TValue>
{
  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; }
  }
}

Passen Sie jedoch auf. Durch Unterklassen und Verwenden von new (da override für den systemeigenen Dictionary-Typ nicht verfügbar ist), wenn ein DictionaryWithDefault -Objekt in ein einfaches Dictionary wird anstelle der Implementierung der Unterklasse die Basisimplementierung Dictionary verwendet (bei Fehlen wird eine Ausnahme ausgelöst).

28
roberocity

Ich habe ein DefaultableDictionary erstellt, um genau das zu tun, wonach Sie fragen!

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;

namespace DefaultableDictionary {
    public class DefaultableDictionary<TKey, TValue> : IDictionary<TKey, TValue> {
        private readonly IDictionary<TKey, TValue> dictionary;
        private readonly TValue defaultValue;

        public DefaultableDictionary(IDictionary<TKey, TValue> dictionary, TValue defaultValue) {
            this.dictionary = dictionary;
            this.defaultValue = defaultValue;
        }

        public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() {
            return dictionary.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator() {
            return GetEnumerator();
        }

        public void Add(KeyValuePair<TKey, TValue> item) {
            dictionary.Add(item);
        }

        public void Clear() {
            dictionary.Clear();
        }

        public bool Contains(KeyValuePair<TKey, TValue> item) {
            return dictionary.Contains(item);
        }

        public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex) {
            dictionary.CopyTo(array, arrayIndex);
        }

        public bool Remove(KeyValuePair<TKey, TValue> 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<TKey> Keys {
            get { return dictionary.Keys; }
        }

        public ICollection<TValue> Values {
            get
            {
                var values = new List<TValue>(dictionary.Values) {
                    defaultValue
                };
                return values;
            }
        }
    }

    public static class DefaultableDictionaryExtensions {
        public static IDictionary<TKey, TValue> WithDefaultValue<TValue, TKey>(this IDictionary<TKey, TValue> dictionary, TValue defaultValue ) {
            return new DefaultableDictionary<TKey, TValue>(dictionary, defaultValue);
        }
    }
}

Dieses Projekt ist ein einfacher Dekorator für ein IDictionary-Objekt und eine Erweiterungsmethode, um die Verwendung zu vereinfachen.

Das DefaultableDictionary ermöglicht das Erstellen eines Wrappers um ein Wörterbuch, das einen Standardwert bereitstellt, wenn versucht wird, auf einen Schlüssel zuzugreifen, der nicht vorhanden ist, oder wenn alle Werte in einem IDictionary aufgelistet werden.

Beispiel: var dictionary = new Dictionary<string, int>().WithDefaultValue(5);

Blogpost auch zur Nutzung.

23
John Sonmez

Nein, so etwas gibt es nicht. Die Erweiterungsmethode ist der richtige Weg, und Ihr Name dafür (GetValueOrDefault) ist eine ziemlich gute Wahl.

4
Patrick Karcher