Firstly, why doesn't Dictionary<TKey, TValue>
support a single null key?
Secondly, is there an existing dictionary-like collection that does?
I want to store an "empty" or "missing" or "default" System.Type
, thought null
would work well for this.
More specifically, I've written this class:
class Switch { private Dictionary<Type, Action<object>> _dict; public Switch(params KeyValuePair<Type, Action<object>>[] cases) { _dict = new Dictionary<Type, Action<object>>(cases.Length); foreach (var entry in cases) _dict.Add(entry.Key, entry.Value); } public void Execute(object obj) { var type = obj.GetType(); if (_dict.ContainsKey(type)) _dict[type](obj); } public static void Execute(object obj, params KeyValuePair<Type, Action<object>>[] cases) { var type = obj.GetType(); foreach (var entry in cases) { if (entry.Key == null || type.IsAssignableFrom(entry.Key)) { entry.Value(obj); break; } } } public static KeyValuePair<Type, Action<object>> Case<T>(Action action) { return new KeyValuePair<Type, Action<object>>(typeof(T), x => action()); } public static KeyValuePair<Type, Action<object>> Case<T>(Action<T> action) { return new KeyValuePair<Type, Action<object>>(typeof(T), x => action((T)x)); } public static KeyValuePair<Type, Action<object>> Default(Action action) { return new KeyValuePair<Type, Action<object>>(null, x => action()); } }
For switching on types. There are two ways to use it:
Switch.Execute(yourObject, Switch.Case<YourType>(x => x.Action()))
switchInstance.Execute(yourObject)
Works great except when you try to add a default case to the "precompiled" version (null argument exception).
Dictionary will hash the key supplie to get the index , in case of null , hash function can not return a valid value that's why it does not support null in key.
The Key value of a Dictionary is unique and doesn't let you add a duplicate key entry.
Dictionary cannot include duplicate or null keys, whereas values can be duplicated or null.
In Dictionary, the key cannot be null, but value can be. In Dictionary, key must be unique. Duplicate keys are not allowed if you try to use duplicate key then compiler will throw an exception. In Dictionary, you can only store same types of elements.
1) Why: As described before, the problem is that Dictionary requires an implementation of the Object.GetHashCode()
method. null
does not have an implementation, therefore no hash code associated.
2) Solution: I have used a solution similar to a NullObject pattern using generics that enables you to use the dictionary seamlessly (no need for a different dictionary implementation).
You can will use it, like this:
var dict = new Dictionary<NullObject<Type>, string>(); dict[typeof(int)] = "int type"; dict[typeof(string)] = "string type"; dict[null] = "null type"; Assert.AreEqual("int type", dict[typeof(int)]); Assert.AreEqual("string type", dict[typeof(string)]); Assert.AreEqual("null type", dict[null]);
You just need to create this struct once in a lifetime :
public struct NullObject<T> { [DefaultValue(true)] private bool isnull;// default property initializers are not supported for structs private NullObject(T item, bool isnull) : this() { this.isnull = isnull; this.Item = item; } public NullObject(T item) : this(item, item == null) { } public static NullObject<T> Null() { return new NullObject<T>(); } public T Item { get; private set; } public bool IsNull() { return this.isnull; } public static implicit operator T(NullObject<T> nullObject) { return nullObject.Item; } public static implicit operator NullObject<T>(T item) { return new NullObject<T>(item); } public override string ToString() { return (Item != null) ? Item.ToString() : "NULL"; } public override bool Equals(object obj) { if (obj == null) return this.IsNull(); if (!(obj is NullObject<T>)) return false; var no = (NullObject<T>)obj; if (this.IsNull()) return no.IsNull(); if (no.IsNull()) return false; return this.Item.Equals(no.Item); } public override int GetHashCode() { if (this.isnull) return 0; var result = Item.GetHashCode(); if (result >= 0) result++; return result; } }
It just hit me that your best answer is probably to just keep track of whether a default case has been defined:
class Switch { private Dictionary<Type, Action<object>> _dict; private Action<object> defaultCase; public Switch(params KeyValuePair<Type, Action<object>>[] cases) { _dict = new Dictionary<Type, Action<object>>(cases.Length); foreach (var entry in cases) if (entry.Key == null) defaultCase = entry.Value; else _dict.Add(entry.Key, entry.Value); } public void Execute(object obj) { var type = obj.GetType(); if (_dict.ContainsKey(type)) _dict[type](obj); else if (defaultCase != null) defaultCase(obj); } ...
The whole rest of your class would remain untouched.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With