为什么 C# 编译器在使用 LINQ 方法 Any() 时创建私有 DisplayClass,我该如何避免?
Why does C# compiler create private DisplayClass when using LINQ method Any() and how can I avoid it?
我有这段代码(整个代码不重要但可以在this link上看到):
internal static class PlayCardActionValidator
{
public static bool CanPlayCard(...)
{
// ...
var hasBigger =
playerCards.Any(
c => c.Suit == otherPlayerCard.Suit
&& c.GetValue() > otherPlayerCard.GetValue());
// ...
}
}
例如,在反编译器 (ILSpy) 中打开代码后,我注意到 C# 编译器存在新创建的 class <>c__DisplayClass0_0
:
如果这段代码对系统的性能不是很重要的话,这对我来说不是问题。此方法被调用了数百万次,垃圾收集器正在清理这些 <>c__DisplayClass0_0
个实例,这会降低性能:
如何在使用 Any
方法时避免创建此 class(他的实例及其垃圾收集)?
为什么 C# 编译器创建这个 class 并且我可以使用 Any()
的任何替代方法?
要理解 "display class" 你必须理解闭包。您在此处传递的 lambda 是一个 闭包 ,一种特殊类型的方法,可以神奇地将状态从它所在的方法的范围拖入 "closes around" 它。
...当然除了没有魔法这样的东西。所有这些状态实际上都必须存在于真实的某个地方,某个与闭包方法相关联并且可以从中轻松获得的地方。您将状态直接与一种或多种方法相关联的编程模式称为什么?
没错:classes. 编译器将 lambda 转换为闭包 class,然后实例化托管内部的 class方法,以便托管方法可以访问 class.
中的状态
避免这种情况发生的唯一方法是不使用闭包。如果这确实会影响性能,请使用老式 FOR
循环而不是 LINQ 表达式。
How can I avoid creating this class (his instances and their garbage collecting) when using the Any method?
Why does the C# compiler creates this class and is there any alternative of Any() I can use?
其他发帖人已经解释了为什么 部分,所以更好的问题是如何避免创建闭包?。答案很简单:如果 lambda 使用 仅 传递的参数 and/or 常量,编译器将不会创建闭包。例如:
bool AnyClub() { return playerCards.Any(c => c.Suit == CardSuit.Club); }
bool AnyOf(CardSuit suit) { return playerCards.Any(c => c.Suit == suit); }
第一个不会创建闭包,而第二个会。
考虑到所有这些,假设您不想使用 for/foreach 循环,您可以创建自己的扩展方法,类似于 [=13] =] 但带有附加参数。对于这种特殊情况,类似这样的事情会起作用:
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;
}
}
并将有问题的代码更改为:
var hasBigger =
playerCards.Any(otherPlayerCard,
(c, opc) => c.Suit == opc.Suit
&& c.GetValue() > opc.GetValue());
我有这段代码(整个代码不重要但可以在this link上看到):
internal static class PlayCardActionValidator
{
public static bool CanPlayCard(...)
{
// ...
var hasBigger =
playerCards.Any(
c => c.Suit == otherPlayerCard.Suit
&& c.GetValue() > otherPlayerCard.GetValue());
// ...
}
}
例如,在反编译器 (ILSpy) 中打开代码后,我注意到 C# 编译器存在新创建的 class <>c__DisplayClass0_0
:
如果这段代码对系统的性能不是很重要的话,这对我来说不是问题。此方法被调用了数百万次,垃圾收集器正在清理这些 <>c__DisplayClass0_0
个实例,这会降低性能:
如何在使用 Any
方法时避免创建此 class(他的实例及其垃圾收集)?
为什么 C# 编译器创建这个 class 并且我可以使用 Any()
的任何替代方法?
要理解 "display class" 你必须理解闭包。您在此处传递的 lambda 是一个 闭包 ,一种特殊类型的方法,可以神奇地将状态从它所在的方法的范围拖入 "closes around" 它。
...当然除了没有魔法这样的东西。所有这些状态实际上都必须存在于真实的某个地方,某个与闭包方法相关联并且可以从中轻松获得的地方。您将状态直接与一种或多种方法相关联的编程模式称为什么?
没错:classes. 编译器将 lambda 转换为闭包 class,然后实例化托管内部的 class方法,以便托管方法可以访问 class.
中的状态避免这种情况发生的唯一方法是不使用闭包。如果这确实会影响性能,请使用老式 FOR
循环而不是 LINQ 表达式。
How can I avoid creating this class (his instances and their garbage collecting) when using the Any method?
Why does the C# compiler creates this class and is there any alternative of Any() I can use?
其他发帖人已经解释了为什么 部分,所以更好的问题是如何避免创建闭包?。答案很简单:如果 lambda 使用 仅 传递的参数 and/or 常量,编译器将不会创建闭包。例如:
bool AnyClub() { return playerCards.Any(c => c.Suit == CardSuit.Club); }
bool AnyOf(CardSuit suit) { return playerCards.Any(c => c.Suit == suit); }
第一个不会创建闭包,而第二个会。
考虑到所有这些,假设您不想使用 for/foreach 循环,您可以创建自己的扩展方法,类似于 [=13] =] 但带有附加参数。对于这种特殊情况,类似这样的事情会起作用:
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;
}
}
并将有问题的代码更改为:
var hasBigger =
playerCards.Any(otherPlayerCard,
(c, opc) => c.Suit == opc.Suit
&& c.GetValue() > opc.GetValue());