我已经看过这个代码示例,看起来它为List分配了一个数组初始化器。我认为它不会起作用但不知何故它编译。 {}不是数组初始值设定项吗?儿童属于IList类型。如果没有花括号之前的“新列表”,它如何工作?
var nameLayout = new StackLayout()
{
HorizontalOptions = LayoutOptions.StartAndExpand,
Orientation = StackOrientation.Vertical,
Children = { nameLabel, twitterLabel }
};
编辑:当我尝试Children = new List<View>{ nameLabel, twitterLabel }
时,编译器会发出以下警告:“无法将属性或索引器Layout.Children分配给它,它是只读的。”
代码片段来自Xamarin:https://developer.xamarin.com/guides/xamarin-forms/getting-started/introduction-to-xamarin-forms/
答案 0 :(得分:10)
这是集合初始化程序的一个特例。
在C#中,数组初始化程序花括号已被推广用于任何集合类构造函数。
如果任何类实现System.Collections.IEnumerable
并且具有一个或多个Add()
方法,那么任何类都支持这些类。 Eric Lippert has a good post about this type of "pattern matching" in C#:编译器在这里所做的是他们所谓的&#34; duck typing&#34;而不是传统的强类型OOP,其中基于继承和接口实现识别类的功能。 C#在一些地方做到这一点。我在那篇文章中有很多东西我都不知道。
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()
重载的意愿是有意义的。我认为能够做到这一点真的很棒,但我对他们选择的语法并不是很满意。如果你发现赋值算子是红鲱鱼,其他人也会。
但我不是语法仲裁者,这对所有相关人员来说可能是最好的。
最后,这也适用于对象初始值设定项:
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
,因为它没有设置器,但我们可以初始化其属性,就像我们可以初始化Foo
中的项目一样。即使它们已经被附加到实际构造函数的不同对象初始化程序初始化,这也是正确的。
正如您所期望的那样,集合初始值设定项是累积的:Bar.Foo
将包含三个项目:{ "1000", "0", "Beringia" }
。
当您将花括号视为赋值语句列或Add()
重载调用的简写时,它们都会成为焦点。
但我同意,在实际上没有分配左值的情况下,等号是不和谐的。
这是我学习from that Eric Lippert article的另一种模式匹配功能:
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
的方法,它返回的东西就像 { {1}}如@ EricLippert对the linked article中IEnumerable
的评论所述(感谢Eric!),并采用foreach
参数。
答案 1 :(得分:5)
在其他两个答案中似乎有一些混淆,说明这实际上是如何运作的。我引用了关于对象初始化器的C#规范部分,它清楚地总结了语义:
在等号后面指定集合初始值设定项的成员初始值设定项是嵌入式集合的初始化。而不是将新集合分配给字段或属性,初始化程序中给出的元素将添加到字段或属性引用的集合中。
请记住,规范是为方便起见而发布的;如果您对C#语言结构的含义有疑问,它(或打印带注释的版本&#34; C#编程语言&#34;)应该是您的第一个参考。
答案 2 :(得分:3)
并非总是如此。你在想的是:
int[] array = new int[] { 1, 2, 3, 4 };
这是阵列初始化器。但是,你也有:
SomeObject obj = new SomeObject { Name = "Hi!", Text = "Some text!" };
这是一个对象初始化器。你的“替换”破坏代码中的内容完全是另一回事 - 集合初始化程序。这适用于任何实现IEnumerable
的类型,并且具有带有正确参数的Add
方法(在这种情况下,类似public void Add(View view)
)。
SomeList list = new SomeList { "Hi!", "There!" };
在您的情况下,使用后两个,并且集合初始值设定项不会实例化新集合。一个简单的示例代码:
void Main()
{
var some = new SomeObject { List = { "Hi!", "There!" } };
some.List.Dump();
}
public class SomeObject
{
public List<string> List { get; private set; }
public SomeObject()
{
List = new List<string>();
}
}
在这种情况下,Main
中的代码大致转换为此等效的C#代码:
var some = new SomeObject();
some.List.Add("Hi!");
some.List.Add("There!");
此表单仅在对象初始值设定项中有效 - 它专门针对您需要使用对象/集合初始化程序语法初始化的readonly
字段的情况而设计。例如,这不起作用:
var some = new SomeObject();
some.List = { "Hi!", "There!" };
如果编译器使用与之前相同的“技巧”,它只能将项添加到集合中 - 但它不能保证集合是空的(尽管在对象初始化器的情况下,这只是“保证”约定“ - 如果你首先用一些项目初始化列表,它们将在”赋值“发生时保留。)
这就是全部,大家:)