C# 7中,C#匿名类型是否多余?

15

自从 C# 7 引入值元组,是否有比元组更适合的有意义的场景?

例如,以下行:

collection.Select((x, i) => (x, i)).Where(y => arr[y.i].f(y.x)).ToArray();

使以下行

collection.Select((x, i) => new {x, i}).Where(y => arr[y.i].f(y.x)).ToArray();

冗余。

在什么情况下,一个比另一个更好地使用(出于性能或优化方面的原因)?

显然,如果需要超过六个字段,元组不能使用,但是是否有一些更微妙的东西呢?


3
易读性问题?new {x, i}让人完全清楚地知道正在返回一个新对象,而我必须比较这两个示例才能弄清楚第一个示例的作用。但也许只是我自己的问题。 - stelioslogothetis
14
一方面,它们得到表达式树的支持,而元组字面值则没有... - Jon Skeet
7
请注意,你的示例在 C# 7.0 中无法编译,因为(x, i)具有未命名的元组元素,但在C# 7.1中它们被隐式命名。 - Jon Skeet
4
匿名类型和元组之间存在客观差异,有些情况下,元组会表现出无法使用或表现更差的情况。因此,我认为这个问题可以纯粹基于事实进行回答,不涉及个人观点,并且不应该被关闭。 - svick
3
@Servy 亲爱的 Servy,我不知道为什么这个问题让你如此坚决地想要将其关闭。也许是由于某种语言障碍导致我措辞不当。为了避免误解,我已经更改了问题的措辞,删除了使用“首选”一词,尽管最初我的意思是指“给出指南”,比如使用驼峰式来命名方法等。我只是想知道两者之间的优缺点以及需要使用其中一个而不是另一个的情况。从你激烈的争论中,我几乎害怕你会选择“关闭并点火”的选项,而不仅仅是“关闭”。 - Igor Ševo
显示剩余11条评论
2个回答

13

匿名类型和C# 7元组之间有各种不同之处,这些不同之处可能使其中一个在某些情况下更合适:

  • C# 7元组是ValueTuple<>。这意味着它们是值类型而匿名类型是引用类型。
  • 元组允许在编译时进行静态类型化,因为它们是可以显式表示的类型。因此,您可以将它们用作方法参数,返回类型等。
  • 匿名类型的成员是实际存在于类型上的属性。元组项是字段
  • 匿名类型的属性有实际名称,而元组上的字段只是命名为ItemN(对于数字N)。标签只是大多由编译器使用的元数据信息,并且不会与实际元组对象一起保存。
  • 因为创建匿名类型实际上在幕后创建了一个类型,所以您可以使用它们来提供一定程度的类型安全性。由于元组只是具有应用类型参数的通用容器,因此您无法完全保证类型安全性。例如,对于大小(int,int)元组将完全兼容位置(int,int)元组,而匿名类型则完全关闭。
  • 正如Jon Skeet所提到的那样,C# 7元组语法目前不支持表达式树。

很好的回答。您会包括Jon Skeet关于元组字面量在表达式树中无法工作的观点吗? - Vadim Ovchinnikov
我喜欢这个答案,因为它提供了关于ValueTuple的实现细节。但它仍然没有回答OP的问题:是否有匿名类型比ValueTuple更好的情况?第5点(就我所理解的)更多地考虑使用普通类型而不是ValueTuple - Vadim Ovchinnikov
@VadimOvchinnikov 当然有一些情况需要区分这两个特性。例如,如果你需要一个例子,可以看看Dapper,在这里你可以传递一个带有命名查询参数的匿名类型。由于元组对象没有关联名称,因此您无法使用它们来实现这一点。 - poke
{btsdaf} - M.kazem Akhgary
{btsdaf} - M.kazem Akhgary

1

@poke目前的答案是正确的,并且指出了元组和匿名类型之间的区别。我将讨论为什么您仍然会使用它们或更喜欢其中之一。

有两个新的C# 7功能,取代了匿名类型。 ValueTuplesRecords

您不使用匿名类型的主要原因是

  • 您无法全局使用匿名类型,只有在本地使用时才能保证类型安全。如果不是本地的,则必须将其视为dynamic对象,这会导致显着的性能开销。

您更喜欢元组而不是匿名类型的原因。

  • 它们在所有地方都是类型安全的(无论命名如何)。

  • 它们可以用作方法参数、类型参数、字段以及几乎任何地方。(是的,我说几乎,有些地方需要采用元组,这只是时间问题。)

  • 由于它们可以用作类型参数,您可能更喜欢将轻量级参数集合封装在单个参数中,例如 Stack<(min, mid, max)>

  • 您可以随时更改项目名称,只要您觉得合适,在通用上下文中,名称 item 可能会满足要求,在更具体的上下文中,您也需要更具体的名称,例如 car

  • 它们可以隐式转换,int, int 可以分配给 (int, long) 而不需要显式转换。

  • 它们在 Deconstruct 中使用,为语言带来了很多语法糖。

  • 您可以进行多个赋值和声明,例如 (int x, int y) = (0, 1)

拥有所有这些特性,仍有一个原因可能使您更喜欢匿名类型而不是元组。
- 匿名类型是引用类型,但元组是值类型。
但是如果您想全局使用匿名类型怎么办? 您更喜欢动态对象还是静态类型对象?
即将推出的Records功能再次击败了匿名类型。 使用记录,您可以以简短,简洁和方便的方式定义类。 这不是什么大问题,只需要一行代码。
public class Point(X, Y);

到处都有类型安全,而且你手头上还有引用类型。这两个新功能使得一切都能打败匿名类型。

请注意,记录还没有添加,我们只需要等待。

唯一剩下真正使用匿名类型的地方将会是

  • 它们仍然作为向后兼容的特性

  • 当您在本地使用匿名类型时,它们可以用于Linq查询。因此,我不认为匿名类型是多余的。

正如我所说的,ValueTuples尚未与每个组件兼容。这只是时间问题,但这就是未来的样子。

足够的争论了。在我看来,使用匿名类型变得越来越少,老程序员可能仍然出于习惯在Linq中使用匿名类型。


匿名类型和元组在不同的情况下使用。例如:在LINQ中,您应该使用匿名类型;如果要使用表达式树,则必须使用匿名类型。元组不支持表达式树。 - Commander

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接