Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dictionary with null key? [duplicate]

Tags:

c#

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:

  1. Statically. Just call Switch.Execute(yourObject, Switch.Case<YourType>(x => x.Action()))
  2. Precompiled. Create a switch, and then use it later with switchInstance.Execute(yourObject)

Works great except when you try to add a default case to the "precompiled" version (null argument exception).

like image 356
mpen Avatar asked Jan 08 '11 07:01

mpen


People also ask

Can dictionary have null keys?

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.

Can a dictionary key be duplicated?

The Key value of a Dictionary is unique and doesn't let you add a duplicate key entry.

Can a dictionary be null?

Dictionary cannot include duplicate or null keys, whereas values can be duplicated or null.

Can a dictionary have duplicate keys C#?

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.


2 Answers

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;     } } 
like image 133
Fabio Marreco Avatar answered Oct 06 '22 10:10

Fabio Marreco


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.

like image 29
Gabe Avatar answered Oct 06 '22 11:10

Gabe