为什么 Dictionary不显示?支持空键吗?
首先,为什么Dictionary
不支持单个空键?
其次,是否有现有的类似字典的集合可以做到这一点?
我想存储一个“空”或“缺失”或“默认”System.Type
,认为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());
}
}
用于切换类型。有两种使用方法:
- 静态。只需调用
Switch.Execute(yourObject, Switch.Case
(x => x.Action())) - 预编译即可。创建一个开关,然后将其与
switchInstance.Execute(yourObject)
一起使用
效果很好除非当您尝试将默认情况添加到“预编译”版本时(空参数例外)。
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:
- Statically. Just call
Switch.Execute(yourObject, Switch.Case<YourType>(x => x.Action()))
- 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).
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(10)
原因:
如前所述,问题在于 Dictionary 需要
Object.GetHashCode()
方法的实现。null
没有实现,因此没有关联的哈希码。解决方案:我使用了类似于使用泛型的 NullObject 模式的解决方案,使您能够无缝地使用字典(不需要不同的字典实现)。
您可以这样使用它:
您只需要在一生中创建一次这个结构:
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.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 use it like this:
You just need to create this struct once in a lifetime :
我突然想到,你最好的答案可能就是跟踪是否定义了默认情况:
班级的其余部分将保持不变。
It just hit me that your best answer is probably to just keep track of whether a default case has been defined:
The whole rest of your class would remain untouched.
它不支持它,因为字典对键进行散列以确定索引,而它不能对空值执行此操作。
一个快速解决方法是创建一个虚拟类,并插入键值??虚拟类实例。
需要更多有关您实际尝试执行的操作的信息,以提供不那么“hacky”的修复
It doesn't support it because the dictionary hashes the key to determine the index, which it can't do on a null value.
A quick fix would be to create a dummy class, and insert the key value ?? dummyClassInstance.
Would need more information about what you're actually trying to do to give a less 'hacky' fix
NameValueCollection 可以采用 null 键。
NameValueCollection could take null key.
如果您确实想要一个允许空键的字典,这是我的快速实现(没有写得很好或没有经过充分测试):
If you really want a dictionary that allows null keys, here's my quick implementation (not well-written or well-tested):
NHibernate 附带了一个 NullableDictionary。那对我来说是这样的。
https://github.com/nhibernate/nhibernate -core/blob/master/src/NHibernate/Util/NullableDictionary.cs
NHibernate comes with a NullableDictionary. That did it for me.
https://github.com/nhibernate/nhibernate-core/blob/master/src/NHibernate/Util/NullableDictionary.cs
字典将对键提供的哈希值进行哈希处理以获取索引,如果为 null ,哈希函数无法返回有效值,这就是为什么它不支持键中为 null 的原因。
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.
在您的情况下,您尝试使用
null
作为标记值(“默认”),而不是实际需要将null
存储为值。与其费力地创建一个可以接受空键的字典,为什么不直接创建自己的哨兵值呢?这是“空对象模式”的变体:请注意,您的第一个 Execute 函数与第二个函数有很大不同。您可能想要这样的东西:
In your case you are trying to use
null
as a sentinel value (a "default") instead of actually needing to storenull
as a value. Rather than go to the hassle of creating a dictionary that can accept null keys, why not just create your own sentinel value. This is a variation on the "null object pattern":Note that your first
Execute
function differs significantly from your second. It may be the case that you want something like this:几天前我遇到了这个线程,需要一个经过深思熟虑且聪明的解决方案来处理空键。我花了时间自己实现了一个来处理更多场景。
您可以在我的 pre- 中找到我目前对 NullableKeyDictionary 的实现发布包 Teronis.NetStandard.Collections (0.1.7-alpha.37)。
实现
用法(Xunit 测试摘录)
Add(..)
存在以下重载:此类的行为应与字典确实如此。
对于
Remove(..)
键,您可以使用以下重载:索引器接受
[AllowNull] KeyType
或NullableKey
。因此,支持的场景,就像其他帖子中所述的那样,是受支持的:我非常感谢反馈和改进建议。 :)
I come across this thread some days ago and needed a well thought out and clever solution to handle null keys. I took the time and implemented one by me to handle more scenarios.
You can find my implementation of NullableKeyDictionary currently in my pre-release package Teronis.NetStandard.Collections (0.1.7-alpha.37).
Implementation
Usage (Excerpt of the Xunit test)
The following overloads exists for
Add(..)
:This class should behave same and intuitive as the dictionary does.
For
Remove(..)
keys you can use the following overloads:The indexers do accept
[AllowNull] KeyType
orNullableKey<KeyType>
. So supported scenarios, like they are stated in other posts, are supported:I highly appreciate feedback and suggestions for improvements. :)
编辑:实际提出的问题的真正答案:为什么不能使用 null 作为 Dictionary的键?
通用字典不支持 null 的原因是
TKey
可能是值类型,不包含 null。要获得这样的功能,您可以使用非通用的
Hashtable
(它使用对象键和值),或者使用DictionaryBase
推出您自己的 Hashtable。编辑:只是为了澄清为什么 null 在这种情况下是非法的,请考虑这个通用方法:
但是当您调用
IsNull(null)
时会发生什么?您会收到编译器错误,因为您无法将
null
转换为int
。我们可以通过说我们只想要可为 null 的类型来修复它:而且,那没问题。限制是我们不能再调用
IsNull
,因为int
不是一个类(可空对象)EDIT: Real answer to the question actually being asked: Why can't you use null as a key for a Dictionary<bool?, string>?
The reason the generic dictionary doesn't support null is because
TKey
might be a value type, which doesn't have null.To get one that does, you could either use the non-generic
Hashtable
(which uses object keys and values), or roll your own withDictionaryBase
.Edit: just to clarify why null is illegal in this case, consider this generic method:
But what happens when you call
IsNull<int>(null)
?You get a compiler error, since you can't convert
null
to anint
. We can fix it, by saying that we only want nullable types:And, that's A-Okay. The restriction is that we can no longer call
IsNull<int>
, sinceint
is not a class (nullable object)