Подтвердить что ты не робот

Какой механизм является лучшим способом расширить словарь для устранения недостающих ключей и почему?

Есть небольшое раздражение, которое я нахожу с большим количеством - у меня есть Dictionary<TKey, TValue>, который содержит значения, которые могут быть или не быть.

Таким образом, нормальное поведение будет заключаться в использовании индексатора, например:

object result = myDictionary["key"];  

Однако, если "key" не находится в словаре, это бросает a KeyNotFoundException, поэтому вы делаете это вместо:

object val;
if (!myDictionary.TryGetValue("key", out val))
{
    val = ifNotFound;
}

Это нормально, за исключением того, что я могу загрузить их в ряд - TryGetValue начинает чувствовать себя ужасно неуклюже.

Итак, опция 1 - это метод расширения:

public static TValue TryGet<TKey, TValue>(
    this Dictionary<TKey, TValue> input, 
    TKey key, 
    TValue ifNotFound = default(TValue))
{
    TValue val;
    if (input.TryGetValue(key, out val))
    {
        return val;
    }

    return ifNotFound;
}

Что позволяет мне делать:

object result = myDictionary.TryGet("key1") ?? ifNotFound;

int i = anotherDictionary.TryGet("key2", -1);

Это достаточно просто, но дополнительный метод расширения с именем, похожим на существующие методы экземпляра, потенциально добавляет путаницу и снижает ремонтопригодность. Он также не согласуется с набором индексатора словарей, который будет обрабатывать отсутствующие ключи.

Таким образом, опция 2 представляет собой новую реализацию IDictionary<TKey, TValue> с неявным отрисовкой из Dictionary<TKey, TValue>, но индексом, который возвращает default(TValue) вместо того, чтобы бросать KeyNotFoundException.

Что мне делать:

ForgivingDictionary<string, object> dict = myDictionary;

object val = dict["key"] ?? ifNotFound;

// do stuff to val, then...
dict["key"] = val;

Итак, значения get и set согласованы, но типы значений более грязные, а ForgivingDictionary содержит намного больше кода.

Оба метода кажутся "грязными" - есть ли лучший способ сделать это уже в .Net?

Оба метода делают компромиссы, которые могут вызвать путаницу, но являются ли еще очевидными/ясными, чем другие? И почему?

4b9b3361

Ответ 1

При назначении имени метода расширения, предназначенного для замены существующего метода, я обычно добавляю к имени метода для специфичности, а не сокращаю его:

GetValueOrDefault(...)

Что касается ForgivingDictionary, вы можете ограничить TKey так, чтобы он не мог быть типом значения. Однако, если вы должны иметь дело с типами значений в нем, вы вернете что-то для типа значения, и лучший вариант - вернуть default(TKey), так как вы не можете вернуть null.

Честно говоря, я бы пошел с расширением.

Изменить: GetValueOrDefault(), конечно, не добавит в словарь, если он не найдет ключ. Я бы просто вернул значение по умолчанию, если оно не было найдено, потому что оно было названо. Если бы кто-то хотел его вставить, хорошее имя было бы GetValueOrInsertDefault().

Ответ 2

Я не могу сделать вывод из вашего вопроса, что делать, если ключ не найден. Я могу представить, что в этом случае ничего не должно быть сделано, но я также могу представить себе обратное. Во всяком случае, изящная альтернатива для ряда этих инструкций TryGetValue, которые вы описываете, использует один из следующих методов расширения. Я предоставил два варианта, в зависимости от того, что нужно делать или нет, когда словарь не содержит ключ:

/// <summary> Iterates over all values corresponding to the specified keys, 
///for which the key is found in the dictionary. </summary>
public static IEnumerable<TValue> TryGetValues<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, IEnumerable<TKey> keys)
{
    TValue value;
    foreach (TKey key in keys)
        if (dictionary.TryGetValue(key, out value))
            yield return value;
}

/// <summary> Iterates over all values corresponding to the specified keys, 
///for which the key is found in the dictionary. A function can be specified to handle not finding a key. </summary>
public static IEnumerable<TValue> TryGetValues<TKey, TValue>(this Dictionary<TKey, TValue> dictionary, IEnumerable<TKey> keys, Action<TKey> notFoundHandler)
{
    TValue value;
    foreach (TKey key in keys)
        if (dictionary.TryGetValue(key, out value))
            yield return value;
        else
            notFoundHandler(key);                        
}

Пример кода о том, как это использовать:

TKey[] keys = new TKey{...};
foreach(TValue value in dictionary.TryGetValues(keys))
{
    //some action on all values here
}

Ответ 3

Или, возможно,

public static TValue TryGet<TKey, TValue>(this Dictionary<TKey, TValue> input, 
                                                   TKey key)
{

     return input.ContainsKey(key) ? input[key] : *some default value*;

}