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";