哪种机制是扩展 Dictionary 来处理丢失键的更好方法?为什么?

发布于 2024-11-11 17:32:35 字数 1564 浏览 2 评论 0原文

我发现自己有一个小烦恼 - 我有一个 Dictionary ,其中包含可能存在或不存在的值。

因此,正常的行为是使用索引器,如下所示:

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 的新实现,具有来自 Dictionary 的隐式转换,但索引器返回 default(TValue ) 而不是抛出 KeyNotFoundException

这让我来做:

ForgivingDictionary<string, object> dict = myDictionary;

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

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

现在获取和设置值是一致的,但值类型更加混乱,并且 ForgivingDictionary 涉及更多代码。

这两种方法看起来都很“混乱”——.Net 中是否有更好的方法来做到这一点?

这两种方法都做出了妥协,可能会导致混乱,但其中一种是否比另一种更明显/清晰?为什么?

There is a minor annoyance I find myself with a lot - I have a Dictionary<TKey, TValue> that contains values that may or may not be there.

So normal behaviour would be to use the indexer, like this:

object result = myDictionary["key"];  

However, if "key" is not in the dictionary this throws a KeyNotFoundException, so you do this instead:

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

Which is fine, except that I can have a load of these in a row - TryGetValue starts to feel awful clunky.

So option 1 is an extension method:

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;
}

Which lets me do:

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

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

Which is simple enough, but an additional extension method with a name similar to existing instance methods potentially adds confusion and reduces maintainability. It's also not consistent with the dictionary's indexer set - that will handle missing keys.

So option 2 is a new implementation of IDictionary<TKey, TValue> with a implicit cast from Dictionary<TKey, TValue> but an indexer that returns default(TValue) instead of throwing a KeyNotFoundException.

That let's me do:

ForgivingDictionary<string, object> dict = myDictionary;

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

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

So now the get and set values are consistent, but value types are messier and ForgivingDictionary involves a lot more code.

Both methods seem 'messy' - is there a better way to do this already in .Net?

Both methods make compromises that could cause confusion, but is one more obvious/clear than the other? And why?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

初熏 2024-11-18 17:32:35

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

GetValueOrDefault(...)

对于 ForgivingDictionary,您可以约束 TKey所以它不能是值类型。但是,如果您必须处理其中的值类型,则将返回值类型的某些内容,并且最好的选择是返回 default(TKey) 因为您无法返回

老实说,我会选择扩展方法。

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

When naming an extension method intended to replace an existing method, I tend to add to the method name for specificity rather than shortening it:

GetValueOrDefault(...)

As for the ForgivingDictionary, you can constrain TKey so that it can't be a value type. However, if you must deal with value types in it, you're going to return something for a value type and the best option is to return default(TKey) since you can't return null.

Honestly, I'd go with the extension method.

Edit: GetValueOrDefault(), of course, wouldn't add to the dictionary if it didn't find the key. I would just return a default value if it wasn't found, because that's how it's named. If one wanted it to insert as well, a good name would be GetValueOrInsertDefault().

冰葑 2024-11-18 17:32:35

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

I'm unable to infer from your question what should be done when a key is not found. I can imagine nothing should be done in that case, but I can also imagine the opposite.
Anyway, an elegant alternative for a series of these TryGetValue-statements you describe, is using one of the following extension methods. I have provided two options, depending on whether something should be done or not when the dictionary does not contain the key:

/// <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);                        
}

Example code on how to use this is:

TKey[] keys = new TKey{...};
foreach(TValue value in dictionary.TryGetValues(keys))
{
    //some action on all values here
}
夏天碎花小短裙 2024-11-18 17:32:35

或者也许

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

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

}

Or perhaps

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

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

}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文