簡體   English   中英

C# 使用,=null, Count > 0. and .Any() 時的最佳實踐

[英]C# Best Practices when using !=null, Count > 0, and .Any()

出於好奇, !=nullCount > 0.Any()之間的根本區別是什么,什么時候是使用它們的最佳時間? - 對於建築和性能。

我知道.Any()適用於IEnumerable s,而不是列表,但我發現自己在允許的情況下可以互換使用它們( !=null and Count > 0 )。 如果這是不好的做法,我不想養成習慣。

Enumerable.Any的工作方式如下

public static bool Any<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
        throw Error.ArgumentNull(nameof (source));
    using (IEnumerator<TSource> enumerator = source.GetEnumerator())
    {
        // Returns very early if there are any items
        if (enumerator.MoveNext())
            return true;
    }
    return false;
}

有一些開銷,但實際上並沒有那么多,因此我假設Any()的性能與List<T>.CountIEnumerable擴展試圖回退到該值,因此這也應該具有可比性).

就清晰度而言,它最終歸結為品味問題。 我個人喜歡Any()的表現力,因為它對我來說更接近自然語言。

rows.Any()

讓我想不到

rows.Count == 0

但不是那么多。

null比較以完全不同的方式工作。 如果您不清楚這一點,您應該在 C# 中了解值和引用類型(警告:最近情況有所不同,因為在當前版本的 C#/.NET 中默認值類型不允許使用null )。

總結:引用類型的存儲方式不同,實際變量(某種程度上)只是指向數據存儲位置的指針(不是 100% 准確)。 C++ 中的經典指針可以采用值0來指示沒有它們指向的數據,這被轉移(至少作為一個概念)到 C#(以及其他類似 C 的語言,如 Java)。 無論如何,由於在 C# 中不直接訪問指針,因此發明了null以指示該變量不指向實際數據(“沒有與該變量關聯的數據”)。 因此,將List<int>變量與null進行比較意味着您詢問列表實例是否已經創建,這是一個必要條件,但對於存儲的實際列表項來說並不是一個充分的條件。

這取決於你的目標。 List可以不是 null,但Count == 0 Any()怎么樣 - 在 LINQ 風格中使用它,例如:

if(MyEnumerable.Any(x => x.Id == myId))
{
    // do something
}
  !=null

檢查列表是否為 null。這可能代表也可能不代表“無項目”。 我建議完全避免將 null 用於列表,因為含義不明確。 使用空集合來表示“沒有項目”。 如果您需要其他東西來表示“不存在”,請使用可能/可選類型,或者如果默認情況下它們不可為空,則使用可能為空的引用。 這應該向讀者發出更明確的信號,即“沒有項目”和“不存在”是可能需要以不同方式處理的不同狀態。

.Any()

這是 LINQ 的一部分,因此適用於任何可枚舉的集合。 非常清晰方便,但會比查屬性稍慢。 但是如果你想檢查某個特定值的存在,它允許內聯過濾。

.Count > 0 or .Length > 0

這僅適用於具有計數/長度屬性的 collections,但應該是最快的,因為它只是進行比較。 至少假設屬性很簡單並且不會做很多工作。

但請注意,除非您非常頻繁地運行代碼,否則性能問題並不是很重要。 因此,在嘗試進行任何優化之前,先測量性能。

TL;DR: Any()實際上會調用ICollection.Count ,如果集合類型實現的話。 否則它將嘗試選擇“最佳”方法來檢查集合是否為非空。

檢查null與檢查空值有很大不同,將它與空值檢查進行比較並沒有什么意義。


Paul Kertscher 已經回答了您的問題的實質,但是為了幫助將來查詢此類問題,值得記住的是,do.net 的源代碼實際上可以在線獲得。

例如,LINQ Any()方法的代碼可以在這里找到: https://github.com/do.net/runtime/blob/main/src/libraries/System.Linq/src/System/Linq/ AnyAll.cs (運行時存儲庫的 rest 中還有更多內容,庫在這里)。 它比 Paul 在他的回答中包含的方法版本稍微復雜一些……他可能引用了不同版本的運行時。 不過,這是最新的實現(原始來源中的評論):

public static bool Any<TSource>(this IEnumerable<TSource> source)
{
    if (source == null)
    {
        ThrowHelper.ThrowArgumentNullException(ExceptionArgument.source);
    }

    if (source is ICollection<TSource> collectionoft)
    {
        return collectionoft.Count != 0;
    }
    else if (source is IIListProvider<TSource> listProv)
    {
        // Note that this check differs from the corresponding check in
        // Count (whereas otherwise this method parallels it).  If the count
        // can't be retrieved cheaply, that likely means we'd need to iterate
        // through the entire sequence in order to get the count, and in that
        // case, we'll generally be better off falling through to the logic
        // below that only enumerates at most a single element.
        int count = listProv.GetCount(onlyIfCheap: true);
        if (count >= 0)
        {
            return count != 0;
        }
    }
    else if (source is ICollection collection)
    {
        return collection.Count != 0;
    }

    using (IEnumerator<TSource> e = source.GetEnumerator())
    {
        return e.MoveNext();
    }
}

ICollection的代碼是System.Collection的一部分,而IListProviderSystem.Linq的一部分。

您可以看到Any()可以通過多種方式來測試底層集合中是否包含任何項目,並且您可以合理地假設它們是按照速度/效率的順序進行嘗試的。 顯然,您可以編寫一個非常棒的ICollection.Count { get }實現,但這將是一件愚蠢的事情,並且很高興認為微軟沒有在 do 附帶的任何集合類型中這樣做.net 運行時。 (順便說一句, arrays通過顯式接口實現ICollection.Count ,將其映射到它們的Length屬性,這就是Any()不需要關心檢查Length的原因)

我還沒有查找IListProvider<T>.GetCount的任何實現。 接口方法的文檔是這樣說的:

如果為真,則只有在快速計算(確定或可能是恆定時間)時才應計算計數,否則應返回 -1。

同樣,糟糕的實現可能存在,但可能不存在。

使用Any()而不是Count通常沒有錯。 調用方法和運行一些 ifs 和強制轉換的額外開銷通常可以忽略不計。 只需確保不要將CountCount()混淆,因為后者可能需要遍歷整個集合,這可能是一件相對昂貴且耗時的事情,如果您只想檢查空虛。

暫無
暫無

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

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