c#中只读属性的对象初始值设定项

Object initializer for readonly properties in c#

如果你有 class:

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

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

您可以使用像这样的对象初始化:

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

如果你有class

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 }) }
}

但是,上面的代码不能编译:

cannot assigne IEnumerable to Bar

我不会写:

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

属性 Bars 是只读的。

可以存档吗?

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

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

除了使用构造函数传入值,或者在构造函数具有 运行.

之后使用常规 AddAddRange 语句之外,别无选择

如果您阅读 实际 编译器错误 (and the docs for collection initializers),您会发现集合初始值设定项是 Add() 调用的语法糖:

CS1950: The best overloaded collection initalizer method System.Collections.Generic.ICollection<Bar>.Add(Bar) has some invalid arguments

CS1503: Argument #1 cannot convert System.Collections.Generic.IEnumerable<Bar> expression to type 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" } }
};

    

但这太恶心了。