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
    }
}

DotNetKicks Image
About these ads