Инициализатор объекта для свойств только для чтения в c # - PullRequest
0 голосов
/ 22 ноября 2018

Если у вас есть класс:

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 to Bar

Я не могу написать:

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

Панель свойств доступна только для чтения.

Может ли это быть в архиве?

Ответы [ 2 ]

0 голосов
/ 22 ноября 2018

Если вы прочитаете фактические ошибки компилятора ( и документы для инициализаторов коллекции ), вы обнаружите, что инициализаторы коллекции являются просто синтаксическим сахаром для вызовов Add():

CS1950: лучший метод инициализации перегруженной коллекции System.Collections.Generic.ICollection<Bar>.Add(Bar) имеет недопустимые аргументы

CS1503: аргумент #1 не может преобразовать выражение System.Collections.Generic.IEnumerable<Bar> в тип Bar

Таким образом, синтаксис SomeCollection = { someItem } будет скомпилирован в SomeCollection.Add(someItem).И вы не можете добавить IEnumerable<Bar> к коллекции Bar s.

Вам необходимо вручную добавить все элементы:

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" } }
};

Но это просто противно.

0 голосов
/ 22 ноября 2018

Bars = { ... } Не выполняет назначение.Вместо этого он вызывает Add для каждого элемента в инициализаторе.Вот почему это не работает.

Именно поэтому Bars = items.Select(s => new Bar { Prop = s }) выдает ту же ошибку: это назначение, а не список для добавления.

Нет другого варианта, который бы использовалконструктор для передачи значений или использования регулярных операторов Add или AddRange после запуска конструктора.

...