So far, we’ve seen useful tricks to extend basic collection types like IEnumerable<T> and ICollection<T> but more complex collections deserve attention too!
Let’s look at IDictionary<T>. Here’s some code I stumbled upon recently; it is a partial implementation of IDataErrorInfo:
public class Order : IDataErrorInfo
{
private IDictionary<string, string> errors =
new Dictionary<string, string>();
private decimal amount;
public decimal Amount
{
get { return amount; }
set
{
amount = value;
if (amount <= 0)
{
errors["Amount"] = "Amount has to be larger than 0";
}
else
{
errors.Remove("Amount");
}
}
}
#region IDataErrorInfo Members
public string Error
{
get { return null; }
}
public string this[string columnName]
{
get
{
if (errors.ContainsKey(columnName))
{
return errors[columnName];
}
else
{
return null;
}
}
}
#endregion
}
The interesting part is:
if (errors.ContainsKey(columnName))
{
return errors[columnName];
}
else
{
return null;
}
What’s the intent of this code? Pretty obvious isn’t it? It checks whether the dictionary has a value for a given key, then returns the value. If the key isn’t found, it returns the default value. This is similar to how the Nullable<T> type works:
int? index = null;
int indexValue = index.GetValueOrDefault();
Here, for obvious reasons, indexValue’s value will equal 0. But what’s interesting is the GetValueOrDefault() method. It clearly expresses the intent as opposed to:
int indexValue = index == null ? 0 : index.Value;
The same thing could apply to dictionaries but hasn’t been provided as part of the Base Class Library:
public string this[string columnName]
{
get { return errors.GetValueOrDefault(columnName); }
}
Here’s Umbrella’s implementation:
public static class DictionaryExtensions
{
public static TValue GetValueOrDefault<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
TKey key)
{
return GetValueOrDefault(dictionary, key, default(TValue));
}
public static TValue GetValueOrDefault<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
TKey key,
TValue defaultValue)
{
return GetValueOrDefault(dictionary, key, () => defaultValue);
}
public static TValue GetValueOrDefault<TKey, TValue>(
this IDictionary<TKey, TValue> dictionary,
TKey key,
Func<TValue> defaultValueProvider)
{
TValue value;
if (!dictionary.TryGetValue(key, out value))
{
value = defaultValueProvider();
}
return value;
}
}
The first method is an overload that will return default(T) if a default value is required.
The second method takes the defaultValue as a parameter
The third method contains the actual implementation and relies on a delegate to obtain the value since the defaultValue could either take some time to process or cannot be known in advance.
Again, these extension methods are small helpers to reduce the friction of th BCL. They are not meant to completely change the programming paradigm. They aren’t intrusive either. They should increase predictability of the source code with a side effect of decreasing the total lines of code count.
() => “Francois”