My Links

News

Welcome to my blog! I'm a Sr. Software Development Engineer in the Seattle area, who has been performing C++/C#/Java development for over 20 years, but have definitely learned that there is always more to learn!

All thoughts and opinions expressed in my blog and my comments are my own and do not represent the thoughts of my employer.

CSharp

Little Wonders

Little Wonders

vNext

Once again, in this series of posts I look at the parts of the .NET Framework that may seem trivial, but can help improve your code by making it easier to write and maintain. The index of all my past little wonders posts can be found here.

Today’s little wonder is another of those small items that can help a lot in certain situations, especially when writing generics. In particular, it is useful in determining what the default value of a given type would be.

The Problem: what’s the default value for a generic type?

There comes a time when you’re writing generic code where you may want to set an item of a given generic type. Seems simple enough, right? We’ll let’s see!

Let’s say we want to query a Dictionary<TKey, TValue> for a given key and get back the value, but if the key doesn’t exist, we’d like a default value instead of throwing an exception.

So, for example, we might have a the following dictionary defined:

1: var lookup = new Dictionary<int, string>

2: {

3: { 1, "Apple" },

4: { 2, "Orange" },

5: { 3, "Banana" },

6: { 4, "Pear" },

7: { 9, "Peach" }

8: };

And using those definitions, perhaps we want to do something like this:

1:// assume a default

2:stringvalue = "Unknown";

3:

4:// if the item exists in dictionary, get its value

5:if (lookup.ContainsKey(5))

6: {

7:value = lookup[5];

8: }

But that’s inefficient, because then we’re double-hashing (once for ContainsKey() and once for the indexer). Well, to avoid the double-hashing, we could use TryGetValue() instead:

1:stringvalue;

2:

3:// if key exists, value will be put in value, if not default it

4:if (!lookup.TryGetValue(5, outvalue))

5: {

6:value = "Unknown";

7: }

But the “flow” of using of TryGetValue() can get clunky at times when you just want to assign either the value or a default to a variable. Essentially it’s 3-ish lines (depending on formatting) for 1 assignment.

So perhaps instead we’d like to write an extension method to support a cleaner interface that will return a default if the item isn’t found:

So this creates an extension method on Dictionary<TKey, TValue> that will attempt to get a value using the given key, and will return the defaultIfNotFound as a stand-in if the key does not exist.

This code compiles, fine, but what if we would like to go one step further and allow them to specify a default if not found, or accept the default for the type? Obviously, we could overload the method to take the default or not, but that would be duplicated code and a bit heavy for just specifying a default. It seems reasonable that we could set the not found value to be either the default for the type, or the specified value.

No, this won’t work either for several reasons. First, we’d expect a reference type to return null, not an “empty” instance. Secondly, not all reference types have a parameter-less constructor (string for example does not). And finally, a constructor cannot be determined at compile-time, while default values can.

The Solution: default(T) – returns the default value for type T

Many of us know the default keyword for its uses in switch statements as the default case. But it has another use as well: it can return us the default value for a given type. And since it generates the same defaults that default field initialization uses, it can be determined at compile-time as well.

Notice that for numeric types the default is 0, and for reference types the default is null. In addition, for struct types, the value is a default-constructed struct – which simply means a struct where every field has their default value (hence 0 Ticks for TimeSpan, etc.).

Now, if defaultIfNotFound is unspecified, it will use default(TValue) which will be the default value for whatever value type the dictionary holds.

So let’s consider how we could use this:

1: lookup.GetValueOrDefault(1); // returns “Apple”

2:

3: lookup.GetValueOrDefault(5); // returns null

4:

5: lookup.GetValueOrDefault(5, “Unknown”); // returns “Unknown”

6:

Again, do not confuse a parameter-less constructor with the default value for a type. Remember that the default value for any type is the compile-time default for any instance of that type (0 for numeric, false for bool, null for reference types, and struct will all default fields for struct).

Consider the difference:

1:// both zero

2:int i1 = default(int);

3:int i2 = newint();

4:

5:// both “zeroed” structs

6: var dt1 = default(DateTime);

7: var dt2 = new DateTime();

8:

9:// sb1 is null, sb2 is an “empty” string builder

10: var sb1 = default(StringBuilder());

11: var sb2 = new StringBuilder();

So in the above code, notice that the value types all resolve the same whether using default or parameter-less construction. This is because a value type is never null (even Nullable<T> wrapped types are never “null” in a reference sense), they will just by default contain fields with all default values.

However, for reference types, the default is null and not a constructed instance. Also it should be noted that not all classes have parameter-less constructors (string, for instance, doesn’t have one – and doesn’t need one).

Summary

Whenever you need to get the default value for a type, especially a generic type, consider using the default keyword. This handy word will give you the default value for the given type at compile-time, which can then be used for initialization, optional parameters, etc.