为什么 C# 集合初始值设定项以这种方式工作?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/459652/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Why do C# collection initializers work this way?
提问by Andrew Hare
I was looking at C# collection initializers and found the implementation to be very pragmatic but also very unlike anything else in C#
我正在查看 C# 集合初始值设定项,发现该实现非常实用,但也与 C# 中的其他任何东西都非常不同
I am able to create code like this:
我能够创建这样的代码:
using System;
using System.Collections;
class Program
{
static void Main()
{
Test test = new Test { 1, 2, 3 };
}
}
class Test : IEnumerable
{
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
public void Add(int i) { }
}
Since I have satisfied the minimum requirements for the compiler (implemented IEnumerable
and a public void Add
) this works but obviously has no value.
由于我已经满足了编译器的最低要求(已实现IEnumerable
和 a public void Add
),因此它可以工作,但显然没有任何价值。
I was wondering what prevented the C# team from creating a more strict set of requirements? In other words why, in order for this syntax to compile, does the compiler not require that the type implement ICollection
? That seems more in the spirit of other C# features.
我想知道是什么阻止了 C# 团队创建一组更严格的要求?换句话说,为什么为了编译这个语法,编译器不要求类型实现ICollection
?这似乎更符合其他 C# 功能的精神。
采纳答案by Bevan
Your observation is spot on - in fact, it mirrors one made by Mads Torgersen, a Microsoft C# Language PM.
您的观察是正确的 - 事实上,它反映了由微软 C# 语言项目经理 Mads Torgersen 所做的观察。
Mads made a post in October 2006 on this subject titled What Is a Collection?in which he wrote:
Mads 在 2006 年 10 月就这个主题发表了一篇题为“什么是收藏?他在其中写道:
Admitted, we blew it in the first version of the framework with System.Collections.ICollection, which is next to useless. But we fixed it up pretty well when generics came along in .NET framework 2.0: System.Collections.Generic.ICollection<T> lets you Add and Remove elements, enumerate them, Count them and check for membership.
Obviously from then on, everyone would implement ICollection<T> every time they make a collection, right? Not so. Here is how we used LINQ to learn about what collections really are, and how that made us change our language design in C# 3.0.
承认,我们在框架的第一个版本中用 System.Collections.ICollection 搞砸了它,几乎没用。但是当泛型出现在 .NET 框架 2.0 中时,我们很好地修复了它:System.Collections.Generic.ICollection<T> 允许您添加和删除元素、枚举它们、计算它们并检查成员资格。
很明显,从那以后,每个人每次创建集合时都会实现 ICollection<T> ,对吧?不是这样。下面是我们如何使用 LINQ 来了解什么是真正的集合,以及这如何使我们改变 C# 3.0 中的语言设计。
It turns out that there are only 14 implementations of ICollection<T>
in the framework, but 189 classes that implement IEnumerable
and have a public Add()
method.
事实证明,ICollection<T>
框架中只有 14 个实现,但 189 个类实现IEnumerable
并具有公共Add()
方法。
There's a hidden benefit to this approach - if they had based it on the ICollection<T>
interface, there would have been exactly one supported Add()
method.
这种方法有一个隐藏的好处——如果他们基于ICollection<T>
接口,就会有一个受支持的Add()
方法。
In contrast, the approach they did take means that the initializers for the collection just form sets of arguments for the Add()
methods.
相比之下,他们采用的方法意味着集合的初始值设定项只是形成Add()
方法的参数集。
To illustrate, let's extend your code slightly:
为了说明,让我们稍微扩展您的代码:
class Test : IEnumerable
{
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
public void Add(int i) { }
public void Add(int i, string s) { }
}
You can now write this:
你现在可以这样写:
class Program
{
static void Main()
{
Test test
= new Test
{
1,
{ 2, "two" },
3
};
}
}
回答by DavidN
I thought about this too, and the answer which satisfies me the most is that ICollection has many methods other than Add, such as: Clear, Contains, CopyTo, and Remove. Removing elements or clearing has nothing to do with being able to support the object initializer syntax, all you need is an Add().
这个我也想过,最让我满意的答案是ICollection除了Add之外还有很多方法,比如:Clear、Contains、CopyTo、Remove。删除元素或清除与能够支持对象初始值设定项语法无关,您只需要一个 Add()。
If the framework was designed granularly enough, and there was an ICollectionAdd interface, then it would've had a "perfect" design. But I honestly don't think that would have added much value, having one method per interface. IEnumerable + Add seems like a hackish approach, but when you think about it, it's a better alternative.
如果框架设计得足够精细,并且有一个 ICollectionAdd 接口,那么它就会有一个“完美”的设计。但老实说,我不认为这会增加多少价值,因为每个接口只有一个方法。IEnumerable + Add 似乎是一种骇人听闻的方法,但仔细想想,这是一个更好的选择。
EDIT: This is not the only time C# has approached a problem with this type of solution. Since .NET 1.1, foreach uses duck typing to enumerate a collection, all your class needs to implement is GetEnumerator, MoveNext and Current. Kirill Osenkov has a postwhich asks your question as well.
编辑:这不是 C# 唯一一次遇到此类解决方案的问题。从 .NET 1.1 开始,foreach 使用鸭子类型来枚举集合,您的类需要实现的只是 GetEnumerator、MoveNext 和 Current。Kirill Osenkov 有一篇帖子也询问了您的问题。
回答by Eldritch Conundrum
(I know I am 3 years late on this, but I was not satisfied with the existing answers.)
(我知道我在这方面迟到了 3 年,但我对现有的答案并不满意。)
why, in order for this syntax to compile, does the compiler not require that the type implement ICollection?
为什么为了编译此语法,编译器不要求类型实现 ICollection?
I'll reverse your question: What use would it be if the compiler had requirements that are not really needed?
我会颠倒你的问题:如果编译器有一些并不真正需要的需求,那有什么用呢?
Non-ICollection
classes too can benefit from the collection initializer syntax. Consider classes that allow adding data into them, without allowing access to previously added data.
非ICollection
类也可以从集合初始值设定项语法中受益。考虑允许向其中添加数据但不允许访问先前添加的数据的类。
Personally, I like to use the new Data { { ..., ... }, ... }
syntax to add a light, DSL-like look to the code of my unit tests.
就我个人而言,我喜欢使用new Data { { ..., ... }, ... }
语法为我的单元测试代码添加一种轻量的、类似于 DSL 的外观。
Actually, I'd rather weaken the requirement, so that I can use the nice-looking syntax without even having to bother implementing IEnumerable
. Collection initializers are pure syntactic sugar to Add(), they shouldn't require anything else.
实际上,我宁愿弱化这个要求,这样我就可以使用漂亮的语法而不必费心去实现IEnumerable
. 集合初始值设定项是 Add() 的纯语法糖,它们不应该需要其他任何东西。