在.NET中,如果您向字典询问与其没有的键相关联的值,则会引发异常。你可以通过调用TryGetValue
来解决这个问题,但我发现它使用的是一个令人厌恶的Ref var。
这意味着您通常必须首先检查密钥是否存在(ContainsKey
)以防止丢失密钥,这看起来很麻烦。
在Ruby中,如果您询问哈希与键相关联的值,则不会出现异常。它只会返回一些默认值,您可以根据需要更改。这使得使用哈希更加愉快。
是否有某些原因导致.NET字典对丢失密钥进行大惊小怪使其更有用?
答案 0 :(得分:12)
问题在于,默认值并不是密钥不在字典中的确定答案。例如,在.Net中,类的默认值为null
。对于任何ref类型,这是一个完全可接受的值,因此可以用作键。
Dictionary<int, string> map = ...;
map.Add(42, null); // OK
让我们假设Dictionary<TKey, TValue>
在密钥丢失时确实返回了默认值。在那个世界中,以下代码将失败
void FillValue(Dictionary<int, string> map, int value) {
if (map[value] == null) {
map.Add(value, "");
}
}
如果value
已存在于字典中且值为null
,则此代码将失败。它会抛出,因为Add
要求值不存在才能正确执行。
底线是,默认值代表缺失的案例会在代码中产生不可避免的歧义。 Dictionary<TKey, TValue>
的当前设计尽可能避免歧义
另请注意,Hashtable
的设计与您的建议一致(缺失值返回null
)。 .Net脱离了这种设计,部分原因是它产生了歧义问题。
答案 1 :(得分:2)
注意:对于那些来自Ruby世界或仅仅是C#世界的人来说,Ruby的地图可以与C#的词典相媲美。
我想说这是理想的,特别是在惯用的C#中。在Ruby中,您可以通过初始化来声明变量,就像您可以从未初始化的键的哈希中检索默认值(或者以相同的方式初始化值)。
红宝石:
some_variable = 40
retrieved_value = some_hash['key'] // returns default string
C#:
int some_variable = 40;
Dictionary<string,string> someDictionary = new Dictionary<string,string>();
string retrievedValue = someDictionary["key"]; // THROWS EXCEPTION
someDictionary.Add("key", "value");
似乎在像C#这样的静态语言中,我们的想法是事先知道某些事情会在尝试之前发挥作用。 C#反而希望您在尝试访问它之前知道存在某些内容,而不是具有回退(如Ruby默认值)。
您可能会认为Ruby哈希检索具有两个行为。如果那里有值,它会检索它。如果不是,则将其初始化为默认值。我认为这就是C#不以这种方式处理它的原因。最重要的是,除非观察到上下文,否则声明的目的(无论是获取现有变量还是故意创建变量)通常都是模棱两可的,即使在上下文中也很难。
然而,有趣的是,C#如何处理赋值到字典。到目前为止显示的所有内容都明确区分了命令式C#和富有表现力的Ruby。但是,在C#中,您可以执行以下操作:
Dictionary<int,int> myDictionary = new Dictionary<int,int>;
myDictionary[3] = 4;
只有在字典中不存在键3
myDictionary.Add(3,4);
在这里,我们看到了Ruby哈希检索中存在的一些潜在的歧义;如果字典没有myDictionary[3] = 4;
条目(将新条目初始化为3
),则4
将执行一项操作;如果条目具有3
条目,则会执行另一项操作({1}}用4)替换它的当前值。
或者,myDictionary.Add(3,4);
如果已有3
条目,则会抛出异常。对我来说,这感觉更像是一种静态命令式语言,我暗示“我知道myDictionary
没有3作为键,现在我正在添加它”而其他语法与Ruby的感觉更少拘押。
编辑:感谢Neolisk
,在dictionary.Add()
和dicitonary[3]=4
之间添加了区分