Какой механизм является лучшим способом расширить словарь, чтобы справиться с отсутствующими ключами и почему? - PullRequest
17 голосов
/ 02 июня 2011

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

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

object result = myDictionary["key"];  

Однако, если "key" нет в словаре, выдается 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 включает в себя намного большеcode.

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

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

Ответы [ 3 ]

8 голосов
/ 02 июня 2011

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

GetValueOrDefault(...)

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

ЧестноЯ бы пошел с методом расширения.

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

1 голос
/ 02 июня 2011

Я не могу понять из вашего вопроса, что делать, если ключ не найден. Я могу представить, что в этом случае ничего нельзя сделать, но я могу представить и обратное. В любом случае, элегантной альтернативой для серии описанных вами операторов 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
}
0 голосов
/ 03 июня 2011

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

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

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

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...