如何制作具有不同返回代码的自定义词典

本文关键字:代码 自定义 返回 何制作 | 更新日期: 2023-09-27 17:53:24

可能重复:
如何获取null而不是KeyNotFoundException按键访问Dictionary值?

我目前在我的项目中有很多Dictionary<string, T>的用途,其中大多数看起来是这样的:

if (myDic.ContainsKey("some key"))
    localVar = myDic["some key"];

它也不是很有效,因为它会对字典进行两次调用,这可能会消耗资源。TryGetValue()是一个很酷的东西,但它并不是在一行中完成的。

如果var v = myDic[key]没有这样的密钥,我只想得到null。我该怎么做?

如何制作具有不同返回代码的自定义词典

您可以使用TryGetValue:的扩展方法

public static U GetValueByKeyOrNull<T, U>(this Dictionary<T, U> dict, T key)
where U : class
{
    U value;
    dict.TryGetValue(key, out value);
    return value;
}

感谢它,你将能够写

somedict.GetValueByKeyOrNull("key1")

最后,为了做到这一点,我使用了一个带有显式接口实现的派生自dictionary类,提出了一个变体:如何通过键访问dictionary值来获得null而不是KeyNotFoundException?

那是

public interface INullValueDictionary<T, U>
    where U : class
{
    U this[T key] { get; }
}
public class NullValueDictionary<T, U> : Dictionary<T, U>, INullValueDictionary<T, U>
    where U : class
{
    U INullValueDictionary<T, U>.this[T key]
    {
        get
        {
            U val;
            dict.TryGet(key, out val);
            return val;
        }
    }
}

并在任何地方使用它而不是原始字典:

//create some dictionary
NullValueDictionary<int, string> dict = new NullValueDictionary<int, string>
{
    {1,"one"}
};
//have a reference to the interface
INullValueDictionary<int, string> idict = dict;
string val = idict[2]; // null
val = idict[1];        // "one"

我不喜欢处理null,所以我的实现看起来像这样:

interface Maybe<T> {
    bool HasValue {get;}
    T Value {get;}
}
class Nothing<T> : Maybe<T> {
    public bool HasValue { get { return false; } }
    public T Value { get { throw new Exception(); } }
    public static const Nothing<T> Instance = new Nothing<T>();
}
class Just<T> : Maybe<T> {
    private T _value;
    public bool HasValue { get { return true; } }
    public T Value { get { return _value; } }
    public Just(T val) {
        _value = val;
    }
}

Maybe是一个可以包含值也可以不包含值的对象。注意,Nothing类包含静态字段Instance。每次我们需要从函数返回Nothing时,我们可以使用这个值,而不是创建新的值。

现在,我们需要创建自己的字典类:

class MyDictionary<TKey, TValue>
{
    private Dictionary<TKey, TValue> _dict;
    ...
    public Maybe<TValue> this[TKey key] {
        TValue val;
        if (_dict.TryGetValue(key, out val)) {
            return new Just<TValue>(val);
        return Nothing<TValue>.Instance;
    }
}

这种方法的优点尚不清楚,因为C#并没有模式匹配。但它可以用dynamic:来模拟

void ProcessResult(Just<string> val) {
    Console.WriteLine(val);
}
void ProcessResult(Nothing<string> n) {
    Console.WriteLine("Key not found");
}
var dict = new MyDictionary<string, string>();
...
dynamic x = dict["key"];
ProcessResult(x);

我认为这是一种非常明确的方式来表达字典不可能总是返回有意义的结果这一事实。此外,对于读者来说,很明显,函数重载ProcessResult(Just<T>)将仅对字典中存在的值调用,而在找不到键的情况下,将调用其他重载。

优点:

  • 类型用作规范
  • 字典可以同时包含值类型和引用类型

缺点:

  • 更多按键
  • 稍微复杂一点

我决定这样做:

class MyDictionary<TKey, TValue> : Dictionary<TKey, TValue>
{
    public new TValue this[TKey key]
    {
        get
        {
            TValue value;
            return TryGetValue(key, out value) ? value : default(TValue);
        }
        set { base[key] = value; }
    }
}

它让我可以像使用其他字典一样,通过方括号使用它。由于我不会将其与值类型TValue一起使用,所以我认为这是一个足够好的解决方案。