7

I have a Dictionary<string, string> as a method argument, and I was wondering if there is a way to make it default to an empty dictionary instead of null. I prefer to always have an empty list/dictionary/IEnumerable instead of null. I tried setting the parameter to:

Dictionary<string, string> dictionary = default(Dictionary<string,string>);

but that evaluates to null.

Is there some way to make the default Dictionary empty?

Default
  • 10,565
  • 8
  • 60
  • 99
Nostradamnit
  • 852
  • 10
  • 20
  • no you can't. default parameter's value should be a compile time constant if you are using optional parameters – Selman Genç Nov 18 '14 at 11:46
  • Your question is a bit unclear.. Why not just use `if (dictionary == null){ dictionary = new Dictionary(); }` - the intention is clear, the caller doesn't mind, no extensions, no overridden behavior.. Why complicate things? – Default Nov 18 '14 at 12:36
  • I changed "parameter" to "argument" btw, I *think* that is what you meant.. See [What's the difference between an argument and a parameter?](http://stackoverflow.com/questions/156767/whats-the-difference-between-an-argument-and-a-parameter) for the difference – Default Nov 18 '14 at 12:40
  • Thank you all for your comments. I guess I'll go with either the null check or the method overload. The reason I even asked the question is that I agree whole-heartedly with this article (https://blog.mariusschulz.com/2014/07/02/stop-cheating-the-type-system) and this quote in it ("If you ever return a null IEnumerable instead of an empty one, I'm going to come to your house and shoot your face with a bazooka.") Null is the crappiest default that could exist, sigh... – Nostradamnit Nov 18 '14 at 12:53

2 Answers2

12

Is there some way to make the default Dictionary empty?

Yes, use the constructor instead of default:

void Foo(Dictionary<string, string> parameter){
    if(parameter == null) parameter = new Dictionary<string,string>();
}

You could also make the parameter optional:

void Foo(Dictionary<string, string> parameter = null)
{
    if(parameter == null) parameter = new Dictionary<string,string>();
}

An optional parameter must be a compile time constant, that's why you can't use new Dictionary<string,string>() directly.


According to the question if you can change the behaviour of the default keyword, no, you cannot return a different value. For reference types null is the default value and will be returned.

C# language specs. §12.2:

The default value of a variable depends on the type of the variable and is determined as follows:

  • For a variable of a value-type, the default value is the same as the value computed by the value-type’s default constructor (§11.1.2).
  • For a variable of a reference-type, the default value is null.

Update: for what it's woth, you could use this extension (i wouldn't use it):

public static T EmptyIfNull<T>(this T coll) 
    where T :  ICollection, new() // <-- Constrain to types with a default constructor and collections
{
    if(coll == null)
        return new T();
    return coll;
}

Now you could use it in this way:

Dictionary<string, string> parameter = null;
Foo(parameter.EmptyIfNull());  // now an empty dictionary is passed

But the last thing another programmer wants to see is thousands of lines of code peppered with .EmptyIfNull() everywhere just because the first guy was too lazy to use a constructor.

Tim Schmelter
  • 411,418
  • 61
  • 614
  • 859
  • i would combine this with the null coalescing operator. – Daniel A. White Nov 18 '14 at 11:45
  • 3
    @DanielA.White And I wouldn't. Why would you? What's in this answer is very easy to read. `parameter = parameter ?? new Dictionary();` redundantly reassigns `parameter`'s prior value to itself, leaving the reader to wonder why that is happening. –  Nov 18 '14 at 11:56
  • Note though that since the OP is interested in making `Foo()` work, this needs a `Foo(Dictionary parameter = null)` default value in addition to the parameter check inside the function body, instead of replaced by the parameter check inside the function body. –  Nov 18 '14 at 11:58
  • @hvd: i've provided another way to make `Foo` "work" without changing the dictionary in the method-body. – Tim Schmelter Nov 18 '14 at 12:24
  • @TimSchmelter Like you, I wouldn't use that. :) I do not think that is what the OP is after, though: the OP is asking for a default, so the caller would not be specifying any parameter value. Your `EmptyIfNull` would force the caller to write `default(Dictionary).EmptyIfNull()` or add a local otherwise-unused variable. But perhaps I'm misunderstanding the question. I've posted a different answer based on my own understanding of the question. –  Nov 18 '14 at 12:28
  • @hvd: you're right, it's not changing the behaviour of `default`(which is not possible). It's just another way to work around the issue which i don't see as a problem at all. – Tim Schmelter Nov 18 '14 at 12:31
  • @AndyT It's too bad that such a good idea as `default` is crippled by being limited to 2 damn branches! I would love to be able to say, in my app, `default(IEnumerable) = new IEnumerable()`. The sun would be brighter, and the world a better place. – Nostradamnit Nov 18 '14 at 12:57
  • @Nostradamnit: No, it wouldn't be. Consider serialisation for persistence which uses `default` to minimise the size of data that should be stored (all `default` values are not stored to save space). Then you or your teammate changes the semantic of `default` and now you can throw your saved data thru the window because it's not backward compatible anymore. – Andriy Tylychko Nov 18 '14 at 14:41
2

Thinking about it, the simple approach with any parameter that you want to give a default value that isn't a compile-time constant works here: do not give it a default value. Use an overloaded function instead.

public void Foo() {
  Foo(new Dictionary<string, string>());
}

public void Foo(Dictionary<string, string> dictionary) {
  ...
}

For the caller, it doesn't really matter how this is implemented: all that matters is that a call Foo() compiles and at run-time has exactly the same effect as Foo(new Dictionary<string, string>()), right? Well, exactly that is what's achieved by adding an overload.