簡體   English   中英

為什么C#編譯器在使用LINQ方法時會創建私有DisplayClass Any()以及如何避免它?

[英]Why does C# compiler create private DisplayClass when using LINQ method Any() and how can I avoid it?

我有這個代碼(整個代碼並不重要,但可以在這個鏈接上看到):

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<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());

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM