是否可以在 C# 6.0 中创建 'self-initializing' 词典扩展方法?

Is it possible to create a 'self-initializing' Dictionary extension method in C# 6.0?

是否可以在 Dictionary 上创建一个扩展方法,如果 Dictionary 为 null,它能够自初始化?

我的想法大致如下:

public static void AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
    // Initialize the Dictionary if null
    dictionary = dictionary ?? new Dictionary<TKey, TValue>();

    // Update or insert value
    dictionary[key] = value;
}

实际使用示例:

// null Dictionary
Dictionary<string, string> dict = null;

// Self-initializing
dict.AddOrUpdate("Key", "Value");

或作为 属性:

// Self-initializing
class.nullDict.AddOrUpdate("Key", "Value");

我认为这对使用它的每个人来说都很混乱,因为对空对象执行操作并不常见。

所以即使可能,我也宁愿使用

Dictionary<string, string> dict = new Dictionary<string, string>();

此外,它现在甚至可以与属性一起使用。

没有

为此,您必须通过 refthat is not allowed 传递参数。

When the this keyword modifies the first parameter of a static method, it signals to the compiler that the method is an extension method. No other modifiers are needed or allowed on the first parameter of an extension method.

你可以,但你必须将 return 类型从 void 更改为 Dictionay,这在我看来是一个非常糟糕的设计。每次使用扩展名时,调用者都必须执行赋值。

public static IDictionary<TKey, TValue> AddOrUpdate<TKey, TValue>(this IDictionary<TKey, TValue> dictionary, TKey key, TValue value)
{
    // Initialize the Dictionary if null
    if (dictionary == null)
        dictionary = new Dictionary<TKey, TValue>();

    // Update value if key already exists
    if (dictionary.ContainsKey(key))
        dictionary[key] = value;
    else
        // Insert value with the given key
        dictionary.Add(key, value);

    return dictionary;
}

我建议更改方法的签名:

// Let's return the dictionary (either passed or created) 
public static IDictionary<TKey, TValue> AddOrUpdate<TKey, TValue>(
    this IDictionary<TKey, TValue> dictionary, 
    TKey key, 
    TValue value) {

    IDictionary<TKey, TValue> result = dictionary;

    // Initialize the Dictionary if null
    if (result == null)
        result = new Dictionary<TKey, TValue>();

    // Update value if key already exists
    if (result.ContainsKey(key))
        result[key] = value;
    else
        // Insert value with the given key
        result.Add(key, value);

    return result;
}

...

Dictionary<string, string> dict = null;

dict = dict.AddOrUpdate("Key", "Value");

你的要求在How can I get an extension method to change the original object?中讨论过,结果是:在引用类型的情况下,你所能做的就是操纵目的。您不能分配它,因为那样您将处理本地副本,而不是最初传递给方法的对象:

public static void Foo(this Object bar)
{
    // assignment
    bar = new ...();

    // here you're NOT calling Baz() on the object passed into the method
    bar.Baz();
}

请注意,这些是引用类型语义。您甚至不能对值类型做任何有意义的事情:

public static void PlusOne(this int number)
{
    number++;
}

这永远不会修改原始值,因为它们是按定义传递的值:此扩展方法对调用它的值的副本进行操作。

话虽如此,这更多是关于 预期 的问题。阅读此代码的任何人:

Dictionary<string, string> dict = null;
dict.AddOrUpdate("Key", "Value");

期望 NullReferenceException(如果 AddOrUpdate()Dictionary<TKey, TValue> 的成员方法)或 ArgumentNullException in the case of an extension method, and even the compiler would expect that.

您认为自己需要这个是有根本原因的。当然,实现 "return the new or updated dictionary from the extension method and assign at every modification" 的答案是可行的,但会给你一个非常笨拙的语法:

Dictionary<string, string> dict = null;
dict = dict.AddOrUpdate("Key", "Value");

而且您只需要忘记不寻常的 dict = 一次 就可以在超出其使用范围的某个时候获得 NRE/ANE。如果您喜欢函数式编程,那可能没问题,但 C# 不是那样。

因此:确保在尝试添加键值对之前初始化字典,并且所有这些不寻常的需求都会消失。可能就这么简单:

Dictionary<string, string> dict = possiblyNullVariable ?? new Dictionary<string, string>();

我想你想要的是一个自初始化属性:

private Dictionary<string, string> dict;

public Dictionary<string, string> Dict => this.dict ?? (this.dict = new Dictionary<string, string>());

那么,无论什么时候使用Dict,它都保证被初始化。