Почему компилятор C# создает закрытый DisplayClass при использовании метода LINQ Any() и как я могу избежать этого?
У меня есть этот код (весь код не важен, но можно увидеть по этой ссылке):
internal static class PlayCardActionValidator
{
public static bool CanPlayCard(...)
{
// ...
var hasBigger =
playerCards.Any(
c => c.Suit == otherPlayerCard.Suit
&& c.GetValue() > otherPlayerCard.GetValue());
// ...
}
}
Например, после открытия кода в декомпиляторе (ILSpy) я заметил существование вновь созданного класса <>c__DisplayClass0_0
компилятором C#:
Это не было бы проблемой для меня, если бы этот код не был критичным для производительности системы. Этот метод вызывается миллионы раз, и сборщик мусора очищает эти <>c__DisplayClass0_0
случаи, которые замедляют производительность:
Как я могу избежать создания этого класса (его экземпляры и сборщик мусора) при использовании Any
метод?
Почему компилятор C# создает этот класс и есть ли альтернатива Any()
Я могу использовать?
2 ответа
Чтобы понять "класс отображения", вы должны понимать замыкания. Лямбда, которую вы передаете здесь, является замыканием, особым типом метода, который волшебным образом перетаскивает состояние из области действия метода, в котором он находится, и "замыкает" его.
... кроме, конечно, что нет такой вещи, как магия. Все это состояние должно жить где-то реально, где-то, что связано с методом замыкания и легко доступно из него. И что вы называете паттерном программирования, где вы связываете состояние напрямую с одним или несколькими методами?
Это верно: классы. Компилятор преобразует лямбду в класс замыкания, а затем создает экземпляр класса внутри метода хостинга, чтобы метод хостинга мог получить доступ к состоянию в классе.
Единственный способ избежать этого - не использовать замыкания. Если это действительно влияет на производительность, используйте старую школу FOR
цикл вместо выражения LINQ.
Как я могу избежать создания этого класса (его экземпляров и их сбора мусора) при использовании метода Any?
Почему компилятор C# создает этот класс и есть ли какая-либо альтернатива Any(), которую я могу использовать?
Другие постеры уже объяснили, почему, поэтому лучший вопрос будет: Как я могу избежать создания замыкания?, И ответ прост: если лямбда использует только переданные параметры и / или константы, компилятор не будет создавать замыкание. Например:
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<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());