简体   繁体   English

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

[英]Object initializer for readonly properties in c#

If you have the class: 如果您有课程:

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

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

You can use a object initialise like: 您可以使用对象初始化,例如:

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

If you have a class 如果你有课

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

You can write 你可以写

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

but, I would like to write something like 但是,我想写些类似的东西

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

However, the code above does not compile with: 但是,以上代码无法与以下代码一起编译:

cannot assigne IEnumerable to Bar 无法将IEnumerable分配给Bar

I cannot write: 我不能写:

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

Property Bars is readonly. 属性栏为只读。

Can this be archived? 可以存档吗?

If you read the actual compiler errors ( and the docs for collection initializers ), you'll find that collection initializers are merly syntactic sugar for Add() calls: 如果您阅读了实际的编译器错误( 以及集合初始值设定项的文档 ),您会发现集合初始值设定项是Add()调用的语法语法糖:

CS1950: The best overloaded collection initalizer method System.Collections.Generic.ICollection<Bar>.Add(Bar) has some invalid arguments CS1950:最佳重载的集合初始化方法System.Collections.Generic.ICollection<Bar>.Add(Bar)有一些无效的参数

CS1503: Argument #1 cannot convert System.Collections.Generic.IEnumerable<Bar> expression to type Bar CS1503:参数#1不能转换System.Collections.Generic.IEnumerable<Bar>表达式输入Bar

So the syntax SomeCollection = { someItem } will be compiled to SomeCollection.Add(someItem) . 因此语法SomeCollection = { someItem }将被编译为SomeCollection.Add(someItem) And you can't add IEnumerable<Bar> to a collection of Bar s. 而且您不能将IEnumerable<Bar>添加到Bar的集合中。

You need to manually add all items: 您需要手动添加所有项目:

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

Or, given shorter code is your goal, do the same in Foo2 's constructor: 或者,鉴于较短的代码是您的目标,请在Foo2的构造函数中执行相同的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);
    }
}

Then you can initialize Foo2 like this: 然后,您可以像这样初始化Foo2:

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

For a funny abuse of the collection initializer syntax as supposed by @JeroenMostert, you could use an extension method: 对于@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);
        }
    }
}

Which allows this: 这允许:

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

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

But that's just nasty. 但这太讨厌了。

Bars = { ... } Doesn't do an assignment. Bars = { ... }不做作业。 Instead it calls Add for every item in the initializer. 而是为初始化程序中的每个项目调用Add That is why it doesn't work. 这就是为什么它不起作用。

That is why Bars = items.Select(s => new Bar { Prop = s }) gives the same error: it is an assignment, not a list to add. 这就是Bars = items.Select(s => new Bar { Prop = s })给出相同错误的原因:这是一个分配,而不是要添加的列表。

There is no option other that using a constructor to pass in the values, or use regular Add or AddRange statements after the constructor has ran. 除了使用构造函数传递值,或者在构造函数运行后使用常规的AddAddRange语句外,没有其他选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM