Why do implicit property names only work in anonymous objects?
限制是什么
1 2 3 4 5 6 7 8
| public class A
{
public string Stuff {get;set ;}
}
...
repository.GetAll().Select(x => new A { x .Stuff }); |
那不管用。你必须添加
1 2 3
| { Stuff = x .Stuff }
repository .GetAll().Select(x => new { x .Stuff }); |
号
但这是可行的。它创建了一个与类A定义非常相似的anon类。
从概念上讲,我看不出这里发生了什么大的变化。有人发光吗?
- 如果x.Stuff和A.Stuff的类型不同怎么办?如果一个可以隐式地转换为另一个呢?另一方面,匿名类型永远不会遇到这样的问题,因为这些类型是在初始化时创建的,成员名称和类型只是从创建anon类型时传递给初始值设定项的属性中获取的。
- 顺便说一下,那不是我的反对票。只是想让你知道。
- 很好的观点,这可能是一个很好的理由来拒绝它。
简短的回答——C编译器和语言团队没有以这种方式实现它——他们或者没有想到(不太可能)或者认为这不是一个好主意……
1
| repository .GetAll().Select(x => new A { x .Stuff }); |
That doesn't work. You have to add
号
这是对象初始值设定项。这是通过调用对象的默认构造函数,然后将属性名与值(即:Stuff = x.Foo匹配)来实现的,它实际上只是匹配属性的快捷方式,因此语法实际上只是用于:
1 2
| A tmp = new A ();
tmp .Stuff = x .Stuff; |
号
现在,我假设编译器团队可以假定一个没有左侧的初始化语句应该搜索一个匹配的属性,其中名称匹配并且类型可以隐式转换,但是我怀疑如果或者当它被语言团队。一般来说,C在语法上相当明确,这将在需要两个单独匹配(name+type)的方式上稍微放宽这一点,并且在许多情况下都不明显。因为您在这里使用的是公共API(A),所以在任意一侧(A或定义为"x"的任何类型)重构也很容易完全破坏它。
最后,这也不是真正必要的-如果您希望以这种方式构造的实例,只需添加一个带有重载的构造函数(在任何情况下,它在许多方面都更安全),然后使用:
1
| repository .GetAll().Select(x => new A (x .Stuff)); |
这使得意图和意义非常明确,并消除了脆弱的可维护性。
1
| repository .GetAll().Select(x => new { x .Stuff }); |
。
这是在做完全不同的事情——在这里,您正在初始化一个匿名类型,并让编译器为您完全确定类型名和类型。我怀疑这是确定为"安全的",因为您从未真正使用过公共API——匿名类型实际上不应该"泄漏"出定义它的方法。在这种情况下,重构更改属性名和有效更改值等的风险会大大降低,并与单个方法隔离,从而使这种"自动"命名功能总体风险更低。另外,这里没有一个简单的替代方法,因为您不能在匿名类型上定义构造函数,所以在这种情况下,没有一个简单的方法来使用简洁的语法。这增加了收益而不会带来很多风险。
- 是的,我只是不喜欢在一个实体上选择*,并且不能真正使用an on来传递您在查询结果中提到的函数。所以我只选择了我在实体上使用的属性来减少带宽。为所有排列创建一个构造器并不适合场景,所以我将坚持使用初始值设定项,尽管它有点脆弱。
- @然而,Steve的危险在于,你正在制造的实体类型实际上是"无效"或错误的,因为它们是不完整的。这通常是个很坏的主意…
- 我不得不不同意,但我理解你的推理。你认为什么不会增加很多原油?
- @Steve它确实依赖于用例——但是在这种类型的场景中,小的DTO很常见。
- 听起来很粗糙。尤其是如果你不想让你的DTO无效或是错误的话,你就必须有一先令的DTO,因为和我们开始这个离题的原因一样。
- 这看起来是一个有趣的方法:devtrends.co.uk/blog/…
一个可能的理由是:如果允许对实际类型进行隐式属性分配,则存储库项的更改(即,将x.Stuff重命名为x.Other)将导致非常令人惊讶的编译时错误,因为新属性不再与a.stuff匹配。
- 我可以想象这是编译器可以警告您的,如果您更改了模式,那么您应该期望在整个代码库中修复问题。
- 里德的回答更能说明问题。请注意,编译器不了解类的语义,因此您需要非常一般地考虑这样的问题——"对于任何可以创建匿名类的地方…"。