此列表初始化程序行为是否应在Visual Studio C#编译器中报告为错误?

时间:2015-10-03 10:25:56

标签: c# .net list compilation initialization

首先,我会说我已经改变了我的设计,不再需要它,但得到一个好的答案仍然会很好

我的代码中有以下classListContainer(附加的代码全部是mcve

class ListContainer
{
    public object ContainedList
    {
        get; private set;
    }

    public int Value
    {
        get; private set;
    }

    public ListContainer(object list, int value)
    {
        ContainedList = list;
        Value = value;
    }
}

在我的代码中的其他class我有一个List<ListContainer>我需要每个ListContainer来包含这个List<ListContainer>,所以我可以像这样实现它:< / p>

//Field in the class
List<ListContainer> mContainers = null;

//In the constructor:
mContainers = new List<ListContainer>();
mContainers.Add(new ListContainer(mContainers, SOME_CONST));
mContainers.Add(new ListContainer(mContainers, SOME_OTHER_CONST));

比它工作正常,但当我尝试使用 列表初始值设定项 时:

//Field in the class
List<ListContainer> mContainers = null;

//In the constructor:
mContainers = new List<ListContainer>
{
    new ListContainer(mContainers, SOME_CONST),
    new ListContainer(mContainers, SOME_OTHER_CONST)
}

你会期望结果是等价的,但实际上结果看起来像是:

mContainers
    [0] - ListContainer
              ContainedList = null
              Value = SOME_CONST
    [1] - ListContainer
              ContainedList = null
              Value = SOME_OTHER_CONST

看到这个结果我已经检查了这个C#编译的输出MSIL并看到了以下代码:

MSIL lists

现在,这解释了问题出现的原因,我甚至在 CSharp语言规范 文档中检出了这是定义的行为:

  

可以按如下方式创建和初始化列表:

var contacts = new List<Contact> {
    new Contact {
        Name = "Chris Smith",
        PhoneNumbers = { "206-555-0101", "425-882-8080" }
    },
    new Contact {
        Name = "Bob Harris",
        PhoneNumbers = { "650-555-0199" }
    }
};
     

具有相同的效果
var __clist = new List<Contact>();
Contact __c1 = new Contact();
__c1.Name = "Chris Smith";
__c1.PhoneNumbers.Add("206-555-0101");
__c1.PhoneNumbers.Add("425-882-8080");
__clist.Add(__c1);
Contact __c2 = new Contact();
__c2.Name = "Bob Harris";
__c2.PhoneNumbers.Add("650-555-0199");
__clist.Add(__c2);
var contacts = __clist;
     

其中__clist,__ c1和__c2是临时变量,否则这些变量将不可见且无法访问。

显然,这种行为是有意的。是否有充分的理由在临时变量上完成所有操作,而不是在原始变量上完成?因为这对我来说似乎是一种错误的行为。

3 个答案:

答案 0 :(得分:4)

原因是避免竞争条件,并发线程访问原始变量,您将添加元素。如果线程访问变量而未添加所有元素,则会出现不一致。

访问同一变量的两个线程因此会得到一个不一致的列表,其中包含不同的元素。

如果将元素添加到不同的行上,这不会令人震惊,但由于您使用了对象初始化程序,因此将对象视为直接初始化其所有元素是正常的,因此需要临时,隐形,变量。

答案 1 :(得分:2)

  

是否有充分的理由在临时列表上完成所有操作而不是原始列表?

没有原始列表:

var __clist = new List<Contact>();
// …
__clist.Add(__c1);
// …
__clist.Add(__c2);
var contacts = __clist;

只创建了一个列表。您可能意味着它是在临时变量而不是原始变量上完成的,但这没有实际效果 - 除了可能更容易实现。如果您认为集合初始化不仅限于变量赋值的上下文,则尤其如此:

SomeMethodCall(new List<int>() { 1, 2, 3 });

由于没有对该列表的引用,实现它的最简单的解决方案就是使用一个保存列表的临时变量,然后将该变量放在初始化器的位置。

对此重要的是旧值被完全覆盖。因此,在mContainers = new List<ListContainer>中,mContainers的旧值永远不会被用于初始化语法的目的。

将集合初始化视为“原子”操作可能是个好主意。整个初始化程序完成后,该列表仅存在(至少对您而言)。因此,您无法在初始化程序中引用自身。

答案 2 :(得分:1)

首先在=的右侧评估作业,然后进行作业。所以mContainers仍为空。