It’s crazy code snippet time again
Here’s an alternative to .NET’s generic Dictionary that allows a null value as a key… It’s one of those things you’ll rarely (never?) need, but when you do you might be glad of it
It comes in two delightful classes; here’s NullableKey:
public static class NullableKey { public static NullableKey<T> Create<T>( T value ) { return new NullableKey<T>( value ); } } public class NullableKey<T> { public T Value { get; set; } public NullableKey( T value ) { this.Value = value; } public override string ToString() { return ( Value != null ) ? Value.ToString() : null; } public static implicit operator NullableKey<T>( T value ) { return new NullableKey<T>( value ); } public static implicit operator T( NullableKey<T> value ) { return value.Value; } }
Here’s NullableDictionary<TKey, TValue> itself and some ToNullableDictionary helpers:
public static class NullableDictionaryExtensions { public static NullableDictionary<TKey, TSource> ToNullableDictionary<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector ) { return source.ToNullableDictionary<TSource, TKey, TSource>( keySelector, s => s ); } public static NullableDictionary<TKey, TElement> ToNullableDictionary<TSource, TKey, TElement>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector ) { return source.ToNullableDictionary<TSource, TKey, TElement>( keySelector, elementSelector, null ); } public static NullableDictionary<TKey, TElement> ToNullableDictionary<TSource, TKey, TElement>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector, Func<TSource, TElement> elementSelector, IEqualityComparer<TKey> comparer ) { var dictionary = new NullableDictionary<TKey, TElement>( comparer ); foreach ( var local in source ) dictionary.Add( keySelector( local ), elementSelector( local ) ); return dictionary; } } public class NullableDictionary<TKey, TValue> : IDictionary<TKey, TValue>, ICollection<KeyValuePair<TKey, TValue>>, IEnumerable<KeyValuePair<TKey, TValue>>, IDictionary, ICollection, IEnumerable { private Dictionary<NullableKey<TKey>, TValue> dict; public NullableDictionary() : this( null ) { } public NullableDictionary( IEqualityComparer<TKey> comparer ) : this( 0, comparer ) { } public NullableDictionary( int capacity, IEqualityComparer<TKey> comparer ) { dict = new Dictionary<NullableKey<TKey>, TValue>( capacity, new EqualityComparer( comparer ) ); } public void Add( TKey key, TValue value ) { dict.Add( NullableKey.Create( key ), value ); } public bool ContainsKey( TKey key ) { return dict.ContainsKey( NullableKey.Create( key ) ); } public ICollection<TKey> Keys { get { return dict.Keys.Select( k => k.Value ).ToList(); } } public bool Remove( TKey key ) { return dict.Remove( NullableKey.Create( key ) ); } public bool TryGetValue( TKey key, out TValue value ) { return dict.TryGetValue( NullableKey.Create( key ), out value ); } public ICollection<TValue> Values { get { return dict.Values; } } public TValue this[ TKey key ] { get { return dict[ NullableKey.Create( key ) ]; } set { dict[ NullableKey.Create( key ) ] = value; } } public int Count { get { return dict.Count; } } public void Clear() { dict.Clear(); } public void CopyTo( KeyValuePair<TKey, TValue>[] array, int arrayIndex ) { throw new NotImplementedException(); } #region ICollection<KeyValuePair<TKey,TValue>> Members void ICollection<KeyValuePair<TKey, TValue>>.Add( KeyValuePair<TKey, TValue> item ) { Add( item.Key, item.Value ); } bool ICollection<KeyValuePair<TKey, TValue>>.Contains( KeyValuePair<TKey, TValue> item ) { return ContainsKey( item.Key ); } int ICollection<KeyValuePair<TKey, TValue>>.Count { get { return dict.Count; } } bool ICollection<KeyValuePair<TKey, TValue>>.IsReadOnly { get { return false; } } bool ICollection<KeyValuePair<TKey, TValue>>.Remove( KeyValuePair<TKey, TValue> item ) { return Remove( item.Key ); } #endregion #region IDictionary Members void IDictionary.Add( object key, object value ) { Add( (TKey)key, (TValue)value ); } bool IDictionary.Contains( object key ) { return ContainsKey( (TKey)key ); } bool IDictionary.IsFixedSize { get { return false; } } bool IDictionary.IsReadOnly { get { return false; } } void IDictionary.Remove( object key ) { Remove( (TKey)key ); } object IDictionary.this[ object key ] { get { return this[ (TKey)key ]; } set { this[ (TKey)key ] = (TValue)value; } } ICollection IDictionary.Keys { get { return Keys.ToList(); } } ICollection IDictionary.Values { get { return Values.ToList(); } } IDictionaryEnumerator IDictionary.GetEnumerator() { return dict.GetEnumerator(); } #endregion #region ICollection Members void ICollection.CopyTo( Array array, int index ) { throw new NotImplementedException(); } object ICollection.SyncRoot { get { return ( (ICollection)dict ).SyncRoot; } } bool ICollection.IsSynchronized { get { return false; } } #endregion #region IEnumerable<KeyValuePair<TKey,TValue>> Members public IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator() { return dict.Select( i => new KeyValuePair<TKey, TValue>( i.Key.Value, i.Value ) ).GetEnumerator(); } #endregion #region IEnumerable Members IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } #endregion private class EqualityComparer : IEqualityComparer<NullableKey<TKey>> { private IEqualityComparer<TKey> comparer; public EqualityComparer() { } public EqualityComparer( IEqualityComparer<TKey> comparer ) { this.comparer = comparer; } #region IEqualityComparer<NullableKey<TKey>> Members public bool Equals( NullableKey<TKey> x, NullableKey<TKey> y ) { return ( comparer != null ) ? comparer.Equals( x.Value, y.Value ) : object.Equals( x.Value, y.Value ); } public int GetHashCode( NullableKey<TKey> obj ) { return ( comparer != null ) ? comparer.GetHashCode( obj.Value ) : ( obj != null && obj.Value != null ) ? obj.Value.GetHashCode() + 1 : 0; } #endregion } }


Leave a comment
Comments feed for this article