Ответ 1
Это частный случай инициализатора коллекции.
В С#, фигурные скобки инициализатора массива были обобщены для работы с любым конструктором класса коллекции.
Любой класс поддерживает те, что реализует System.Collections.IEnumerable
и имеет один или несколько методов Add()
. Эрик Липперт имеет хорошую статью об этом типе "сопоставления шаблонов" в С#: то, что делает компилятор здесь, это то, что они называют " duck typing", а не обычный строго типизированный OOP, где возможности класса распознаются на основе наследования и реализации интерфейса. С# делает это в нескольких местах. В этой статье я не знал много материала.
public class Foo : List<String>
{
public void Add(int n)
{
base.Add(n.ToString());
}
public void Add(DateTime dt, double x)
{
base.Add($"{dt.ToShortDateString()} {x}");
}
}
И затем это компилируется:
var f = new Foo { 0, 1, 2, "Zanzibar", { DateTime.Now, 3.7 } };
Этот синтаксический сахар для этого:
var f = new Foo();
f.Add(0);
f.Add(1);
f.Add(2)
f.Add("Zanzibar");
f.Add(DateTime.Now, 3.7);
С этими играми можно играть в довольно странные игры. Я не знаю, неплохо ли все исправить (на самом деле я знаю - это не так), но вы можете. Я написал класс парсера командной строки, где вы можете определить параметры через инициализатор коллекции. Он получил более дюжины перегрузок Add
с переменными списками параметров, многие из которых носят общий характер. Все, что может сделать компилятор, - честная игра.
Опять же, вы можете подтолкнуть это к уменьшению отдачи к проблеме злоупотребления функциями.
То, что вы видите, - это расширение того же синтаксиса инициализатора, где оно позволяет вам инициализировать коллекцию для не назначаемого члена, который уже создал сам класс:
public class Bar
{
public Foo Foo { get; } = new Foo();
}
И теперь...
var b = new Bar { Foo = { 0, "Beringia" } };
{ 0, "Beringia" }
- инициализатор коллекции для экземпляра Foo
, который Bar
создан для себя; это синтаксический сахар для этого:
var b = new Bar();
b.Foo.Add(0);
b.Foo.Add("Beringia");
Желание компилятора разрешить перегрузки Foo.Add()
в использовании инициализатора синтаксиса-сахара имеет смысл, когда вы смотрите на него таким образом. Я считаю, что это здорово, но я не на 100% удобен с синтаксисом, который они выбрали. Если вы обнаружили, что оператор присваивания является красной селедкой, другие тоже.
Но я не Арбитр Синтаксиса, и это, вероятно, лучше всего для всех.
Наконец, это также работает с инициализаторами объектов:
public class Baz
{
public String Name { get; set; }
}
public class Bar
{
public Foo Foo { get; } = new Foo { 1000 };
public Baz Baz { get; } = new Baz { Name = "Initial name" };
}
Итак...
var b = new Bar { Foo = { 0, "Beringia" }, Baz = { Name = "Arbitrary" } };
Что на самом деле превращается в...
var b = new Bar();
b.Foo.Add(0);
b.Foo.Add("Beringia");
b.Baz.Name = "Arbitrary";
Мы не можем инициализировать Bar.Baz
, потому что у него нет setter, но мы можем инициализировать его свойства так же, как мы можем инициализировать элементы в Foo
. И это правда, даже если они уже были инициализированы другим инициализатором объекта, прикрепленным к фактическому конструктору.
Инициализаторы коллекции, как и следовало ожидать, являются кумулятивными: Bar.Foo
будет иметь три элемента: { "1000", "0", "Beringia" }
.
Когда вы думаете о фигурных скобках как сокращении для столбца операторов присваивания или Add()
перегрузки вызовов, все это фокусируется.
Но я согласен, что знак равенства jarring в случаях, когда lvalue на самом деле не назначается.
Bonus
Вот еще одна функция сопоставления шаблонов, которую я узнал о из этой статьи Эрика Липперта:
public static class HoldMyBeerAndWatchThis
{
public static IEnumerable<int> Select(Func<String, String> f)
{
yield return f("foo").Length;
}
}
Поэтому...
var x = from s in HoldMyBeerAndWatchThis select s;
Все, что вам нужно для select
, чтобы работать, - это то, что вещь, которую вы выбираете, должна иметь метод с именем select
, который возвращает что-то такое, что quacks как IEnumerable
, как указано в примечаниях @EricLippert на foreach
в связанная статья (спасибо Эрик!) и принимает параметр Func<T,T>
.