问题
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
I cannot write:
var foo = new Foo2 {
Bars = items.Select(s => new Bar { Prop = s })
}
Property Bars is readonly.
Can this be archived?
回答1:
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:
CS1950: The best overloaded collection initalizer method
System.Collections.Generic.ICollection<Bar>.Add(Bar)
has some invalid argumentsCS1503: Argument
#1
cannot convertSystem.Collections.Generic.IEnumerable<Bar>
expression to typeBar
So the syntax SomeCollection = { someItem }
will be compiled to SomeCollection.Add(someItem)
. And you can't add IEnumerable<Bar>
to a collection of Bar
s.
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:
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:
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:
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.
回答2:
Bars = { ... }
Doesn't do an assignment. Instead it calls Add
for every item in the initializer. 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.
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.
来源:https://stackoverflow.com/questions/53431659/object-initializer-for-readonly-properties-in-c-sharp