简体   繁体   中英

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

Is it possible to create an extension method on a Dictionary, that is able to self-initialize if the Dictionary is null?

I'm thinking something along the lines of the following:

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

Practical use example:

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

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

Or as a property:

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

No

For this to work, you would have to pass your parameter by ref and that 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.

What you're asking for is discussed in How can I get an extension method to change the original object? , and the outcome is: in the case of a reference type, all you can do is manipulate the object. You can't assign it, because then you'll be working on a local copy and not the object originally passed into the method:

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

Note that those are reference type semantics. You can't even do anything meaningful to value types:

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

This will never modify the original value, as those are passed by value by definition: this extension method operates on a copy of the value it was called with.

That being said, this is more a question about expectations . Anyone reading this code:

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

Would expect either a NullReferenceException (if AddOrUpdate() were a member method of Dictionary<TKey, TValue> ) or an ArgumentNullException in the case of an extension method , and even the compiler would expect that .

There's an underlying reason you think you need this. Sure, implementing the answers that say "return the new or updated dictionary from the extension method and assign at every modification" works, but gives you an awfully clumsy syntax:

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

And you'll only have to forget the unusual dict = once to get a NRE/ANE at some point beyond its usage. That's maybe fine if you're into functional programming, but C# is not that.

So: make sure that the dictionary is initialized before trying to add key-value pairs and the need for all this unusualness goes away. It may be as simple as this:

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

I would think that this is very confusing for everyone who is using it, since performing operations on null objects is not something common.

So even if it's possible, I would rather use

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

Moreover it even works with properties now.

You could but you would have to change the return type from void to Dictionay which would be a very poor design IMO. The caller would then have to execute an assignment every time the extension is used.

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

I suggest changing method's signature :

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

I think what you want is a self-initializing property:

private Dictionary<string, string> dict;

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

Then, whenever you use Dict , it's guaranteed to be initialized.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM