C#中只读属性的对象初始化器

时间:2018-11-22 13:03:21

标签: c# projection readonly object-initializers

如果您有课程:

class Foo {
      Bar Bar { get; } = new Bar();
}

class Bar {
      string Prop {get; set; }
}

您可以使用对象初始化,例如:

var foo = new Foo { 
    Bar = { Prop = "Hello World!" }
}

如果您有课程

class Foo2 {
      ICollection<Bar> Bars { get; } = new List<Bar>();
}

您可以写

var foo = new Foo2 { 
    Bars = { 
        new Bar { Prop = "Hello" }, 
        new Bar { Prop = "World" }
    }
}

但是,我想写些类似的东西

var items = new [] {"Hello", "World"};
var foo = new Foo2 { 
    Bars = { items.Select(s => new Bar { Prop = s }) }
}

但是,上面的代码无法与以下代码一起编译:

  

无法将IEnumerable分配给Bar

我不能写:

var foo = new Foo2 { 
    Bars = items.Select(s => new Bar { Prop = s })
}

属性栏为只读。

可以存档吗?

2 个答案:

答案 0 :(得分:2)

Bars = { ... }不执行作业。相反,它为初始化程序中的每个项目调用Add。这就是为什么它不起作用的原因。

这就是Bars = items.Select(s => new Bar { Prop = s })给出相同错误的原因:这是一个分配,而不是要添加的列表。

除了使用构造函数传递值或在构造函数运行后使用常规AddAddRange语句外,没有其他选择。

答案 1 :(得分:2)

如果您阅读了 actual 编译器错误(and the docs for collection initializers),则会发现集合初始化程序是Add()调用的语法上的甜言蜜语:

  

CS1950:最佳的重载集合initalizer方法System.Collections.Generic.ICollection<Bar>.Add(Bar)具有一些无效的参数

     

CS1503:参数#1无法将System.Collections.Generic.IEnumerable<Bar>表达式转换为类型Bar

因此语法SomeCollection = { someItem }将被编译为SomeCollection.Add(someItem)。而且您无法将IEnumerable<Bar>添加到Bar的集合中。

您需要手动添加所有项目:

foreach (bar in items.Select(s => new Bar { Prop = s }))
{
    foo.Bars.Add(bar);
}

或者,鉴于较短的代码是您的目标,请在Foo2的构造函数中执行相同的操作:

public class Foo2 
{
    public ICollection<Bar> Bars { get; }

    public Foo2() : this(Enumerable.Empty<Bar>()) { }

    public Foo2(IEnumerable<Bar> bars)
    {
        Bars = new List<Bar>(bars);
    }
}

然后您可以像这样初始化Foo2:

var foo = new Foo2(items.Select(...));

对于@JeroenMostert假设的集合初始化器语法的有趣滥用,您可以使用扩展方法:

public static class ICollectionExtensions
{
    public static void Add<T>(this ICollection<T> collection, IEnumerable<T> items)
    {
        foreach (var item in items)
        {
            collection.Add(item);
        }
    }
}

哪个允许:

public class Foo
{
    public ICollection<string> Bar { get; } = new List<string>();
}

var foo = new Foo
{
    Bar = { new [] { "foo", "bar", "baz" } }
};

但这太讨厌了。