I don't like to deal with null
so my implementation will look like this:
interface Maybe<T> {
bool HasValue {get;}
T Value {get;}
}
class Nothing<T> : Maybe<T> {
public bool HasValue { get { return false; } }
public T Value { get { throw new Exception(); } }
public static const Nothing<T> Instance = new Nothing<T>();
}
class Just<T> : Maybe<T> {
private T _value;
public bool HasValue { get { return true; } }
public T Value { get { return _value; } }
public Just(T val) {
_value = val;
}
}
Maybe
is a object that can contain value or not. Note that Nothing
class contains static field Instance
. We can use this value instead of creating new value each time we need to return Nothing
from function.
Now, we need to create our own dictionary class:
class MyDictionary<TKey, TValue>
{
private Dictionary<TKey, TValue> _dict;
...
public Maybe<TValue> this[TKey key] {
TValue val;
if (_dict.TryGetValue(key, out val)) {
return new Just<TValue>(val);
return Nothing<TValue>.Instance;
}
}
Advantage of this approach is not clear, because C# doesn't have pattern matching. But it can be emulated with dynamic
:
void ProcessResult(Just<string> val) {
Console.WriteLine(val);
}
void ProcessResult(Nothing<string> n) {
Console.WriteLine("Key not found");
}
var dict = new MyDictionary<string, string>();
...
dynamic x = dict["key"];
ProcessResult(x);
I think that this is very clear way to express the fact that dictionary can't always return meaningful result. Also it is obvious for reader that function overload ProcessResult(Just<T>)
will be called only for values that present in dictionary and other overload will be called in case when key is not found.
Pros:
- Type serves as a specification.
- Dictionary can contain both value and reference types.
Cons:
- More keystrokes.
- Little more complexity to deal with.