Nik*_*tov 29 c# linq performance instance any
我有这个代码(整个代码并不重要,但可以在这个链接上看到):
internal static class PlayCardActionValidator
{
public static bool CanPlayCard(...)
{
// ...
var hasBigger =
playerCards.Any(
c => c.Suit == otherPlayerCard.Suit
&& c.GetValue() > otherPlayerCard.GetValue());
// ...
}
}
Run Code Online (Sandbox Code Playgroud)
在反编译器(ILSpy)中打开代码后,我注意到<>c__DisplayClass0_0C#编译器存在新创建的类:
如果此代码对系统性能不重要,这对我来说不是问题.此方法被调用数百万次,垃圾收集器正在清理这些<>c__DisplayClass0_0实例,这会降低性能:
在使用该Any方法时,如何避免创建此类(他的实例和垃圾收集)?
为什么C#编译器会创建这个类,有没有Any()我可以使用的替代方法?
Mas*_*ler 37
要理解"显示类",您必须了解闭包.你在这里传递的lambda是一个闭包,一种特殊类型的方法,它从它所处方法的范围中神奇地拖拽状态并"绕过"它.
......当然除了没有魔法这样的东西.所有这个状态实际上都必须存在于真实的某个地方,这个地方与封闭方法有关并且可以从中获得.您将编程模式称为直接将状态与一个或多个方法关联的是什么?
那是对的:上课. 编译器将lambda转换为闭包类,然后在托管方法中实例化类,以便托管方法可以访问类中的状态.
不发生这种情况的唯一方法是不使用闭包.如果这确实影响了性能,请使用旧式FOR循环而不是LINQ表达式.
Iva*_*oev 23
在使用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); }
Run Code Online (Sandbox Code Playgroud)
第一个将不会创建一个闭包,而第二个将.
考虑到所有这些,并假设您不想使用for/foreach循环,您可以创建自己的扩展方法,类似于System.Linq.Enumerable其他参数.对于这种特殊情况,这样的事情会起作用:
public static class Extensions
{
public static bool Any<T, TArg>(this IEnumerable<T> source, TArg arg, Func<T, TArg, bool> predicate)
{
foreach (var item in source)
if (predicate(item, arg)) return true;
return false;
}
}
Run Code Online (Sandbox Code Playgroud)
并将有问题的代码更改为:
var hasBigger =
playerCards.Any(otherPlayerCard,
(c, opc) => c.Suit == opc.Suit
&& c.GetValue() > opc.GetValue());
Run Code Online (Sandbox Code Playgroud)