协变对象初始化器?

时间:2011-01-23 12:52:05

标签: c# .net object-initializers

说我有一个具有字典< string,bool>属性的类,使用对象初始化程序我可以使用这种语法(我觉得看起来很干净):

new MyClass()
{
  Table = { {"test",true},{"test",false} }
}

但是,在初始化程序之外我不能这样做:

this.Table = { {"test",true},{"test",false} };

为什么初始化程序是特例?我猜测它与LINQ要求,协方差或诸如此类的东西有关,但感觉有点不一致,无法在任何地方使用那种初始化器......

3 个答案:

答案 0 :(得分:11)

问题有些令人困惑,因为问题与LINQ无关,与泛型方差无关,并且具有集合初始值设定项以及对象初始值设定项。真正的问题是,据我所知,“为什么在对象创建表达式之外使用集合初始化程序是不合法的?

这里的相关设计原则是,一般来说,我们希望创建和初始化对象的操作在其中包含单词“new”作为向读者发出的信号,即此处发生了对象创建。 (是的,这个规则在C#中有一些例外。作为读者的练习,看看你是否可以将它们全部命名。)

按照自己的方式做事会使代码更难以推理。快点,这是做什么的?

d = new List<int>() { 10, 20, 30 };
d = { 40, 50, 60 };

第二行是否将 40,50,60附加到现有列表中?或者用新的列表替换旧列表?那里没有“新”,那么读者是否期望创建一个新对象?

当你说

q = new Whatever() { MyList = { 40, 50, 60 } };

不会创建新列表;它将40,50,60附加到构造函数分配的现有列表中。因此,您建议的语法是否含糊不清,并且是否创建了新列表。

建议的功能既令人困惑又不必要,因此不太可能很快实施。

答案 1 :(得分:2)

此限制远比LINQ早。即使回到C,你也可以写

int numbers[5] = {1, 2, 3, 4, 5};

但您无法使用此语法为数组指定值。

我对C#背后的原因的猜测是,通常你不应该对两个不同的对象使用相同的引用。如果您需要为现有引用分配新集合,很可能您没有很好地设计代码,您可以根据定义初始化集合,也可以使用两个单独的引用而不是一个。

答案 2 :(得分:0)

考虑到您的语法在运行时抛出NullReferenceException - 您确定可以使用它吗?

public class Test
{
  public Dictionary<string, bool> Table {get; set;}
}

public void TestMethod()
{
  Test t = new Test { Table = { {"test", false} } }; //NullReferenceException
}

这编译为以下(通过反射器):

Test <>g__initLocal3 = new Test();
<>g__initLocal3.Table.Add("test", 0.0M);

如您所见,Table未初始化,因此在运行时生成NullReferenceException

如果在Test的ctor中创建Dictionary,则类初始值设定项会生成Add个语句的级联,这是初始值设定项中的语法糖(对于IEnumerable s)。< / p>

由于我们无法看到或想象的不可预知的副作用,这可能不是针对普通代码引入的。 Eric Lippert可能会提供帮助,因为他可能对手头的事情有了更多的了解。