为什么C#编译器在使用LINQ方法时会创建私有DisplayClass Any()以及如何避免它?

我有这个代码(整个代码并不重要,但可以在这个链接上看到):

internal static class PlayCardActionValidator { public static bool CanPlayCard(...) { // ... var hasBigger = playerCards.Any( c => c.Suit == otherPlayerCard.Suit && c.GetValue() > otherPlayerCard.GetValue()); // ... } } 

例如,在反编译器(ILSpy)中打开代码之后,我注意到C#编译器存在新创建的类c__DisplayClass0_0

在此处输入图像描述

如果此代码对系统性能不重要,对我来说这不是问题。 这个方法被调用数百万次,垃圾收集器正在清理这些c__DisplayClass0_0实例,这会降低性能:

在此处输入图像描述

在使用Any方法时,如何避免创建此类(他的实例和垃圾收集)?

为什么C#编译器创建这个类,我可以使用任何替代的Any()吗?

要理解“显示类”,您必须了解闭包。 你在这里传递的lambda是一个闭包 ,一种特殊类型的方法,它从它所处方法的范围中神奇地拖拽状态并“绕过”它。

……当然除了没有魔法这样的东西。 所有这个状态实际上都必须存在于真实的某个地方,这个地方与封闭方法有关并且可以从中获得。 那么你将状态与一个或多个方法直接关联的编程模式又是什么?

那是对的: 课程。 编译器将lambda转换为闭包类,然后在托管方法中实例化类,以便托管方法可以访问类中的状态。

不发生这种情况的唯一方法是不使用闭包。 如果这确实影响了性能,请使用旧式FOR循环而不是LINQ表达式。

在使用Any方法时,如何避免创建此类(他的实例和垃圾收集)?

为什么C#编译器会创建这个类,我可以使用任何替代的Any()吗?

其他海报已经解释了为什么部分,所以更好的问题是如何避免创建闭包? 。 答案很简单:如果lambda 使用传递的参数和/或常量,编译器将不会创建闭包。 例如:

 bool AnyClub() { return playerCards.Any(c => c.Suit == CardSuit.Club); } bool AnyOf(CardSuit suit) { return playerCards.Any(c => c.Suit == suit); } 

第一个不会创建一个闭包而第二个会创建一个闭包。

考虑到所有这些,并假设您不想使用for / foreach循环,您可以创建自己的扩展方法,类似于System.Linq.Enumerable的扩展方法,但具有其他参数。 对于这种特殊情况,这样的事情会起作用:

 public static class Extensions { public static bool Any(this IEnumerable source, TArg arg, Func predicate) { foreach (var item in source) if (predicate(item, arg)) return true; return false; } } 

并将有问题的代码更改为:

 var hasBigger = playerCards.Any(otherPlayerCard, (c, opc) => c.Suit == opc.Suit && c.GetValue() > opc.GetValue()); 
Interesting Posts