为什么.NET词典会发牢骚?

时间:2013-12-17 19:00:29

标签: .net vb.net dictionary

在.NET中,如果您向字典询问与其没有的键相关联的值,则会引发异常。你可以通过调用TryGetValue来解决这个问题,但我发现它使用的是一个令人厌恶的Ref var。

这意味着您通常必须首先检查密钥是否存在(ContainsKey)以防止丢失密钥,这看起来很麻烦。

在Ruby中,如果您询问哈希与键相关联的值,则不会出现异常。它只会返回一些默认值,您可以根据需要更改。这使得使用哈希更加愉快。

是否有某些原因导致.NET字典对丢失密钥进行大惊小怪使其更有用?

2 个答案:

答案 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之间添加了区分