Подтвердить что ты не робот

Почему компилятор С# создает частный 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__DisplayClass0_0, что замедляет производительность:

введите описание изображения здесь

Как я могу избежать создания этого класса (его экземпляры и сбор мусора) при использовании метода Any?

Почему компилятор С# создает этот класс и есть ли альтернатива Any(), которую я могу использовать?

4b9b3361

Ответ 1

Чтобы понять "класс отображения", вы должны понимать закрытие. Лямбда, которую вы проходите здесь, - это закрытие, особый тип метода, который волшебным образом перетаскивается из состояния метода в него и "закрывает" его.

... кроме того, что нет такой вещи, как магия. Все это состояние должно фактически жить где-то реально, где-то, что связано с методом закрытия и легко доступно из него. И что вы называете шаблоном программирования, где вы напрямую связываете состояние с одним или несколькими методами?

Это право: классы. Компилятор преобразует лямбда в класс замыкания, а затем создает экземпляр класса внутри метода хостинга, поэтому метод хостинга может получить доступ к состоянию в классе.

Единственный способ избежать этого - не использовать закрытие. Если это действительно влияет на производительность, используйте цикл старой школы FOR вместо выражения LINQ.

Ответ 2

Как я могу избежать создания этого класса (его экземпляры и сбор мусора) при использовании метода Any?

Почему компилятор С# создает этот класс и есть ли какая-либо альтернатива Any(), которую я могу использовать?

Другие плакаты уже объяснили часть почему, поэтому лучший вопрос будет Как я могу избежать создания закрытия?. И ответ прост: если лямбда использует только переданные параметры и/или константы, компилятор не создаст закрытие. Например:

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

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

Первое не будет создавать замыкание, а второе будет.

Учитывая все это и полагая, что вы не хотите использовать циклы для /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());