17

我发现自己有一个小烦恼——我有一个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 中有没有更好的方法来做到这一点?

这两种方法都做出了可能导致混淆的妥协,但一种比另一种更明显/清晰吗?为什么?

4

3 回答 3

8

在命名旨在替换现有方法的扩展方法时,我倾向于在方法名称中添加特定性而不是缩短它:

GetValueOrDefault(...)

至于ForgivingDictionary,您可以进行约束TKey,使其不能是值类型。但是,如果您必须处理其中的值类型,您将为值类型返回一些东西,最好的选择是返回default(TKey),因为您不能返回null

老实说,我会使用扩展方法。

编辑GetValueOrDefault()当然,如果没有找到密钥,它就不会添加到字典中。如果找不到,我只会返回一个默认值,因为它就是这样命名的。如果还想插入它,一个好名字就是GetValueOrInsertDefault().

于 2011-06-02T13:49:33.863 回答
1

我无法从您的问题中推断出找不到密钥时应该做什么。我可以想象在这种情况下什么都不应该做,但我也可以想象相反的情况。无论如何,您描述的一系列这些 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
}
于 2011-06-02T13:38:26.830 回答
0

也许

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

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

}
于 2011-06-03T12:52:25.337 回答