C# 带空键的字典?

C# 带空键的字典?,c#,C#,首先,为什么字典不支持单个空键 第二,是否有一个现有的类似词典的集合可以这样做 我想存储一个“空”或“缺少”或“默认”的系统。键入,认为null可以很好地解决这个问题 更具体地说,我写了这个类: class Switch { private Dictionary<Type, Action<object>> _dict; public Switch(params KeyValuePair<Type, Action<object>>

首先,为什么
字典
不支持单个空键

第二,是否有一个现有的类似词典的集合可以这样做

我想存储一个“空”或“缺少”或“默认”的
系统。键入
,认为
null
可以很好地解决这个问题


更具体地说,我写了这个类:

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());
    }
}
类开关
{
私人词典;
公共交换机(参数KeyValuePair[]个案例)
{
_dict=新字典(cases.Length);
foreach(案例中的var条目)
_dict.Add(entry.Key,entry.Value);
}
公共无效执行(对象obj)
{
var type=obj.GetType();
如果(_dict.ContainsKey(类型))
_dict[类型](obj);
}
publicstaticvoidexecute(objectobj,params KeyValuePair[]案例)
{
var type=obj.GetType();
foreach(案例中的var条目)
{
if(entry.Key==null | | type.IsAssignableFrom(entry.Key))
{
输入值(obj);
打破
}
}
}
公共静态KeyValuePair案例(操作)
{
返回新的KeyValuePair(typeof(T),x=>action());
}
公共静态KeyValuePair案例(操作)
{
返回新的KeyValuePair(typeof(T),x=>action((T)x));
}
公共静态KeyValuePair默认值(操作)
{
返回新的KeyValuePair(null,x=>action());
}
}
用于打开类型。有两种使用方法:

  • 静态地。只需调用
    Switch.Execute(您的对象,Switch.Case(x=>x.Action())
  • 预编译的。创建一个开关,然后稍后将其与
    switchInstance.Execute(yourObject)

  • 除非您尝试将默认情况添加到“预编译”版本(空参数异常),否则效果很好。

    编辑:实际问题的真实答案:

    泛型词典不支持null的原因是
    TKey
    可能是一个值类型,它没有null

    new Dictionary<int, string>[null] = "Null"; //error!
    
    但是当您调用
    IsNull(null)
    时会发生什么呢


    好的。限制是我们不能再调用
    IsNull
    ,因为
    int
    不是一个类(可为null的对象)

    可以接受null键。

    它不支持它,因为字典对键进行散列以确定索引,而它不能对null值执行此操作

    快速修复方法是创建一个虚拟类,并插入键值??dummyClassInstance。
    需要更多关于您实际试图做什么的信息,以提供一个更少的“黑客”修复

    字典将哈希键提供以获取索引,如果为null,哈希函数无法返回有效值,这就是为什么它不支持key中的null。

    在您的情况下,您试图使用
    null
    作为哨兵值(a“默认值”)而不是实际需要将
    null
    存储为值。与其麻烦地创建一个可以接受null键的字典,为什么不创建自己的sentinel值呢?这是“null对象模式”的一个变体:


    如果您真的想要一个允许空键的字典,下面是我的快速实现(编写得不好或测试得不好):

    类NullableDict:IDictionary
    {
    Dictionary dict=新字典();
    V nullValue=默认值(V);
    bool hasNull=false;
    公共NullableDict()
    {
    }
    公共无效添加(K键,V值)
    {
    if(key==null)
    if(hasNull)
    抛出新的ArgumentException(“复制键”);
    其他的
    {
    空值=值;
    hasNull=true;
    }
    其他的
    dict.Add(键、值);
    }
    公共bool ContainsKey(K键)
    {
    if(key==null)
    返回hasNull;
    返回dict.ContainsKey(键);
    }
    公共ICollection密钥
    {
    得到
    {
    如果(!hasNull)
    返回指令键;
    List keys=dict.keys.ToList();
    添加(默认值(K));
    返回新的只读集合(键);
    }
    }
    公共布尔删除(K键)
    {
    if(key!=null)
    返回dict.Remove(键);
    bool oldHasNull=hasnul;
    hasNull=false;
    返回oldHasNull;
    }
    公共布尔TryGetValue(K键,输出V值)
    {
    if(key!=null)
    返回dict.TryGetValue(键,输出值);
    value=hasNull?null值:默认值(V);
    返回hasNull;
    }
    公共ICollection值
    {
    得到
    {
    如果(!hasNull)
    返回指令值;
    列表值=dict.values.ToList();
    值。添加(空值);
    返回新的ReadOnlyCollection(值);
    }
    }
    公共V本[K键]
    {
    得到
    {
    if(key==null)
    if(hasNull)
    返回空值;
    其他的
    抛出新的KeyNotFoundException();
    其他的
    返回dict[键];
    }
    设置
    {
    if(key==null)
    {
    空值=值;
    hasNull=true;
    }
    其他的
    dict[键]=值;
    }
    }
    公共作废添加(KeyValuePair项)
    {
    添加(item.Key、item.Value);
    }
    公共空间清除()
    {
    hasNull=false;
    格言(Clear);
    }
    public bool包含(KeyValuePair项)
    {
    如果(item.Key!=null)
    返回((ICollection)目录)。包含(项);
    if(hasNull)
    返回EqualityComparer.Default.Equals(null值,item.Value);
    返回false;
    }
    public void CopyTo(KeyValuePair[]数组,int-arrayIndex)
    {
    ((ICollection)dict.CopyTo(数组,数组索引);
    if(hasNull)
    数组[arrayIndex+dict.Count]=新的KeyValuePair(默认值(K),nullValue);
    }
    公众的
    
    bool IsNull<T> (T value) {
        return value == null;
    }
    
    Argument '1': cannot convert from '<null>' to 'int'
    
    bool IsNull<T> (T value) where T : class {
        return value == null;
    }
    
    class Switch
    {
        private class DefaultClass { }
    
        ....
    
        public void Execute(object obj)
        {
            var type = obj.GetType();
            Action<object> value;
            // first look for actual type
            if (_dict.TryGetValue(type, out value) ||
            // look for default
                _dict.TryGetValue(typeof(DefaultClass), out value))
                value(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 == typeof(DefaultClass) || type.IsAssignableFrom(entry.Key))
                {
                    entry.Value(obj);
                    break;
                }
            }
        }
    
        ...
    
        public static KeyValuePair<Type, Action<object>> Default(Action action)
        {
            return new KeyValuePair<Type, Action<object>>(new DefaultClass(), x => action());
        }
    }
    
        public void Execute(object obj)
        {
            Execute(obj, (IEnumerable<KeyValuePair<Type, Action<object>>>)_dict);
        }
    
        public static void Execute(object obj, params KeyValuePair<Type, Action<object>>[] cases)
        {
            Execute(obj, (IEnumerable<KeyValuePair<Type, Action<object>>>)cases);
        }
    
        public static void Execute(object obj, IEnumerable<KeyValuePair<Type, Action<object>>> cases)
        {
            var type = obj.GetType();
            Action<object> defaultEntry = null;
            foreach (var entry in cases)
            {
                if (entry.Key == typeof(DefaultClass))
                    defaultEntry = entry.Value;
                if (type.IsAssignableFrom(entry.Key))
                {
                    entry.Value(obj);
                    return;
                }
            }
            if (defaultEntry != null)
                defaultEntry(obj);
        }
    
    class NullableDict<K, V> : IDictionary<K, V>
    {
        Dictionary<K, V> dict = new Dictionary<K, V>();
        V nullValue = default(V);
        bool hasNull = false;
    
        public NullableDict()
        {
        }
    
        public void Add(K key, V value)
        {
            if (key == null)
                if (hasNull)
                    throw new ArgumentException("Duplicate key");
                else
                {
                    nullValue = value;
                    hasNull = true;
                }
            else
                dict.Add(key, value);
        }
    
        public bool ContainsKey(K key)
        {
            if (key == null)
                return hasNull;
            return dict.ContainsKey(key);
        }
    
        public ICollection<K> Keys
        {
            get 
            {
                if (!hasNull)
                    return dict.Keys;
    
                List<K> keys = dict.Keys.ToList();
                keys.Add(default(K));
                return new ReadOnlyCollection<K>(keys);
            }
        }
    
        public bool Remove(K key)
        {
            if (key != null)
                return dict.Remove(key);
    
            bool oldHasNull = hasNull;
            hasNull = false;
            return oldHasNull;
        }
    
        public bool TryGetValue(K key, out V value)
        {
            if (key != null)
                return dict.TryGetValue(key, out value);
    
            value = hasNull ? nullValue : default(V);
            return hasNull;
        }
    
        public ICollection<V> Values
        {
            get
            {
                if (!hasNull)
                    return dict.Values;
    
                List<V> values = dict.Values.ToList();
                values.Add(nullValue);
                return new ReadOnlyCollection<V>(values);
            }
        }
    
        public V this[K key]
        {
            get
            {
                if (key == null)
                    if (hasNull)
                        return nullValue;
                    else
                        throw new KeyNotFoundException();
                else
                    return dict[key];
            }
            set
            {
                if (key == null)
                {
                    nullValue = value;
                    hasNull = true;
                }
                else
                    dict[key] = value;
            }
        }
    
        public void Add(KeyValuePair<K, V> item)
        {
            Add(item.Key, item.Value);
        }
    
        public void Clear()
        {
            hasNull = false;
            dict.Clear();
        }
    
        public bool Contains(KeyValuePair<K, V> item)
        {
            if (item.Key != null)
                return ((ICollection<KeyValuePair<K, V>>)dict).Contains(item);
            if (hasNull)
                return EqualityComparer<V>.Default.Equals(nullValue, item.Value);
            return false;
        }
    
        public void CopyTo(KeyValuePair<K, V>[] array, int arrayIndex)
        {
            ((ICollection<KeyValuePair<K, V>>)dict).CopyTo(array, arrayIndex);
            if (hasNull)
                array[arrayIndex + dict.Count] = new KeyValuePair<K, V>(default(K), nullValue);
        }
    
        public int Count
        {
            get { return dict.Count + (hasNull ? 1 : 0); }
        }
    
        public bool IsReadOnly
        {
            get { return false; }
        }
    
        public bool Remove(KeyValuePair<K, V> item)
        {
            V value;
            if (TryGetValue(item.Key, out value) && EqualityComparer<V>.Default.Equals(item.Value, value))
                return Remove(item.Key);
            return false;
        }
    
        public IEnumerator<KeyValuePair<K, V>> GetEnumerator()
        {
            if (!hasNull)
                return dict.GetEnumerator();
            else
                return GetEnumeratorWithNull();
        }
    
        private IEnumerator<KeyValuePair<K, V>> GetEnumeratorWithNull()
        {
            yield return new KeyValuePair<K, V>(default(K), nullValue);
            foreach (var kv in dict)
                yield return kv;
        }
    
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    }
    
    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);
        }
    
    ...
    
    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]);
    
    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;
        }
    }
    
    var dict = new NullableKeyDictionary<Type, string>
    dict[typeof(int)] = "int type";
    dict[typeof(string)] = "string type";
    
    dict[null] = "null type";
    // Or:
    dict[NullableKey<Type>.Null] = "null type";