今天 (2025-04-06) 早上在 Facebook - 台灣 .NET 技術愛好找論壇裡看到 Will 保哥轉貼了一則 X 貼文,
要檢查一個集合物件是否有元素在內,會使用哪一種語法來檢查 …
在 C# 開發中,經常需要判斷一個集合是否有包含任何元素。雖然這是一個簡單的判斷,但其實背後有不少值得探討的細節。就來瞭解這四種常見的做法,並分析它們的優缺點與適合的使用情境。
原文出處

四種判斷方式
作者「Oleg Kyrylchuk」列出了四種判斷 List 不為 null 且有元素的方式
- Classic way
- List.Count way
- Enumerable.Any way
- Pattern matching way
1. 傳統 Count 判斷法
if (list != null && list.Count > 0)
優點
- 適用於 List<T>、Array 等實作了 ICollection 的類型
- Count 為 O(1),效能穩定
缺點
- 不適用於 IEnumerable<T>(如延遲查詢的 LINQ)
2. Null-safe Count 判斷法
if (list?.Count > 0)
優點
- 語法簡潔,能處理 null
- 與上面一樣是 O(1) 操作
缺點
- 僅適用於實作 ICollection 的集合
3. 使用 Any()
if (list?.Any() == true)
優點
- 適用於所有 IEnumerable<T>,包括延遲查詢
- 口語化的表達:「是否有任何元素?」
缺點
- 效能最差情況為 O(n),但通常只需檢查第一筆
4. C# 9 Pattern Matching 寫法
if (list is { Count: > 0 })
優點
- 語法簡潔
- 支援 null 並為 O(1) 操作
- 適合喜歡新語法表示方式的開發者
缺點
- 僅適用於 C# 9 或更新版本
- 僅適用於 ICollection
- 不認識或不熟悉新語法的開發者會不適應
建議使用的情境
整理一下不同情境下的使用建議
集合型別 | 建議寫法 |
List, Array | Count > 0 或 Pattern Matching |
IEnumerable | 使用 Any() |
Lazy LINQ 查詢 | 使用 Any() |
喜歡比較新的語法 | Pattern Matching |
需支援 null 判斷 | ?.Count > 0 或 Any() |
小結
- 如果集合型別已知是 List<T>、Array 等 ➜ 用 .Count 屬性。
- 如果來源是 LINQ 查詢或無法確認類型 ➜ 使用 .Any() 判斷是否有資料更合適。
時間複雜度 O(1) 是什麼意思?
當我們說一個操作是 O(1),表示這個操作無論資料量有多大,執行時間幾乎不變,是「常數時間」操作。
- 不管集合有幾個元素,這個操作花費的時間都差不多。
- 不會因為清單變長,執行時間就變長。
舉個例子:
如果使用的是 list.Count
如果 list 是 List<T>、Array、或實作了 ICollection 的集合,那 Count 屬性只是回傳一個欄位值,這是一個已經計算好的值:
public int Count => _count;
所以取得這個值是 O(1),非常快,不會隨著資料筆數變多而變慢。
如果你用的是 list.Any(),這種會透過 foreach 檢查第一筆資料的操作:
public static bool Any<T>(this IEnumerable<T> source)
{
foreach (var item in source)
{
return true;
}
return false;
}
雖然平均來說也很快(通常一筆就結束),但最壞情況下還是可能要走完整個集合(例如空集合),所以它是 O(n)。
Big-O 表示「當輸入資料量變大時,操作的執行時間(或空間)會怎麼成長」。
例如:
list.Count; // O(1)
list.Any(); // O(n)
list.Contains(x) // O(n)
常見的時間複雜度 Big-O 分類
Big-O | 名稱 | 說明(舉例) |
O(1) | 常數時間 | 不管資料有多少,時間幾乎不變。如:讀取 .Count 屬性 |
O(n) | 線性時間 | 跑一次整個資料。如:.Any() 對 IEnumerable |
O(n²) | 平方時間 | 巢狀迴圈,如兩層 for 迴圈比較所有組合 |
O(log n) | 對數時間 | 如:Binary Search(二分搜尋) |
O(n log n) | 線性對數時間 | 常見於排序(如 QuickSort、MergeSort) |
O(2ⁿ) | 指數時間 | 演算法會爆炸性成長,常見於暴力遞迴 |
Big-O 關心的不是「實際執行時間」,而是「當資料變多時,會變幾倍慢?」
- O(1):1 筆、10 萬筆都差不多快(例如讀取變數)
- O(n):資料越多,越慢(例如掃描 list)
- O(n²):資料多一點,時間暴增(例如比對所有組合)
以下列出有關 Big O notation 的相關資料
- Wiki - 大O符号
- 那些聽起來很專業的「演算法 Algorithm」跟「Big O notation」到底是什麼? | by 00如是說 | Coding Fighter | Medium
- 演算法 | Big O 複雜度| Pseudocode 偽代碼 | 越南放大鏡 X 下班資工系
有關 .Count 屬性與 .Count() 方法的差異
在 C# 中,.Count 與 .Count() 雖然名字相似,實際上是兩種不同的實作方式,各自適用於不同場景。
.Count 屬性
- 屬於 ICollection 或 ICollection<T> 接口
- 適用於:List<T>、Array、HashSet<T> 等
- O(1) 時間複雜度,直接取得現成的欄位值
- 效能最佳,無需遍歷集合
var list = new List<int> { 1, 2, 3 };
int count = list.Count; // 直接取得欄位值,快速
.Count() 方法
- 屬於 LINQ 擴充方法:System.Linq.Enumerable.Count()
- 適用於所有 IEnumerable<T>,包括 lazy 資料來源
- O(n) 時間複雜度,需要遍歷整個集合(除非最佳化)
- 常見於處理延遲查詢時
IEnumerable<int> query = Enumerable.Range(1, 1000).Where(x => x % 2 == 0);
int count = query.Count(); // 遍歷整個集合
讓 ChatGPT 寫個 Benchmark 程式碼
這邊是寫在 LINQPad 8.x 裡面的程式碼,然後使用 BenchmarkDotNet 的 NuGet 套件(可透過 LINQPad 的 F4 -> Add NuGet 加入)

void Main()
{
// 執行 benchmark
var config = DefaultConfig.Instance;
BenchmarkRunner.Run<CollectionCheckBenchmarks>(config);
}
[MemoryDiagnoser]
[SimpleJob(RuntimeMoniker.Net80)]
public class CollectionCheckBenchmarks
{
// 測試兩種資料大小:10 與 1_000_000
[Params(10, 1_000_000)]
public int Size;
private List<int> list; // 實體集合(支援 ICollection)
private IEnumerable<int> lazy; // 延遲查詢集合(僅支援 IEnumerable)
// 測試前先初始化集合
[GlobalSetup]
public void Setup()
{
list = Enumerable.Range(1, Size).ToList();
lazy = Enumerable.Range(1, Size).Where(x => x > 0); // 使用 Where 模擬 lazy source
}
// 各種判斷方式對 list 的效能測試
[Benchmark] public bool Count_List() => CheckCount(list);
[Benchmark] public bool NullConditional_List() => CheckNullConditional(list);
[Benchmark] public bool Any_List() => CheckAny(list);
[Benchmark] public bool PatternMatching_List() => CheckPatternMatching(list);
// lazy enumerable 僅能使用 Any()
[Benchmark] public bool Any_Lazy() => CheckAny(lazy);
// 使用 is ICollection<T> 並檢查 Count
private static bool CheckCount<T>(IEnumerable<T> source)
{
if (source is ICollection<T> collection)
{
return collection.Count > 0;
}
return false;
}
// 使用 null 條件運算子檢查 Count
private static bool CheckNullConditional<T>(IEnumerable<T>? source)
{
try
{
return (source as ICollection<T>)?.Count > 0;
}
catch
{
return false;
}
}
// 使用 Any() 方法(適用所有 IEnumerable,遇第一筆即返回)
private static bool CheckAny<T>(IEnumerable<T>? source)
{
return source?.Any() == true;
}
// 使用 C# 9 pattern matching 檢查 Count
private static bool CheckPatternMatching<T>(IEnumerable<T>? source)
{
return source is ICollection<T> { Count: > 0 };
}
}
P.S. 如果要使用 LINQPad 執行 Benchmark.NET 的程式,記得要調整設定

最後跑出來的結果
// * Summary *
BenchmarkDotNet v0.14.0, Windows 11 (10.0.26100.3624)
11th Gen Intel Core i7-1165G7 2.80GHz, 1 CPU, 8 logical and 4 physical cores
.NET SDK 9.0.102
[Host] : .NET 8.0.12 (8.0.1224.60305), X64 RyuJIT AVX-512F+CD+BW+DQ+VL+VBMI
Job=.NET 8.0 Runtime=.NET 8.0
| Method | Size | Mean | Error | StdDev | Gen0 | Allocated |
|--------------------- |-------- |----------:|----------:|----------:|-------:|----------:|
| Count_List | 10 | 2.067 ns | 0.0642 ns | 0.0600 ns | - | - |
| NullConditional_List | 10 | 2.234 ns | 0.0196 ns | 0.0163 ns | - | - |
| Any_List | 10 | 2.735 ns | 0.0777 ns | 0.0832 ns | - | - |
| PatternMatching_List | 10 | 2.079 ns | 0.0641 ns | 0.0877 ns | - | - |
| Any_Lazy | 10 | 27.044 ns | 0.5593 ns | 0.8371 ns | 0.0153 | 96 B |
| Count_List | 1000000 | 2.028 ns | 0.0478 ns | 0.0424 ns | - | - |
| NullConditional_List | 1000000 | 2.243 ns | 0.0255 ns | 0.0226 ns | - | - |
| Any_List | 1000000 | 2.656 ns | 0.0288 ns | 0.0225 ns | - | - |
| PatternMatching_List | 1000000 | 1.989 ns | 0.0083 ns | 0.0070 ns | - | - |
| Any_Lazy | 1000000 | 26.317 ns | 0.5421 ns | 0.7049 ns | 0.0153 | 96 B |
// * Warnings *
MultimodalDistribution
CollectionCheckBenchmarks.Any_Lazy: .NET 8.0 -> It seems that the distribution is bimodal (mValue = 3.27)
// * Hints *
Outliers
CollectionCheckBenchmarks.NullConditional_List: .NET 8.0 -> 2 outliers were removed (3.46 ns, 3.49 ns)
CollectionCheckBenchmarks.PatternMatching_List: .NET 8.0 -> 3 outliers were removed (3.71 ns..4.08 ns)
CollectionCheckBenchmarks.Count_List: .NET 8.0 -> 1 outlier was removed (3.33 ns)
CollectionCheckBenchmarks.NullConditional_List: .NET 8.0 -> 1 outlier was removed (3.59 ns)
CollectionCheckBenchmarks.Any_List: .NET 8.0 -> 3 outliers were removed (4.06 ns..4.18 ns)
CollectionCheckBenchmarks.PatternMatching_List: .NET 8.0 -> 2 outliers were removed (3.29 ns, 3.45 ns)
CollectionCheckBenchmarks.Any_Lazy: .NET 8.0 -> 2 outliers were removed (31.62 ns, 32.41 ns)
// * Legends *
Size : Value of the 'Size' parameter
Mean : Arithmetic mean of all measurements
Error : Half of 99.9% confidence interval
StdDev : Standard deviation of all measurements
Gen0 : GC Generation 0 collects per 1000 operations
Allocated : Allocated memory per single operation (managed only, inclusive, 1KB = 1024B)
1 ns : 1 Nanosecond (0.000000001 sec)
// * Diagnostic Output - MemoryDiagnoser *
// ***** BenchmarkRunner: End *****
Run time: 00:05:09 (309.58 sec), executed benchmarks: 10
Global total time: 00:05:10 (310.37 sec), executed benchmarks: 10
Benchmark 結果整理(.NET 8.0)
以下請 ChatGPT 幫我解說 Summary 內容
方法 | Size | Mean (ns) | 備註 |
Count(list) | 10 | 2.07 ns | 最快,穩定 O(1) |
NullConditional(list) | 10 | 2.23 ns | 類似 Count,但加了 null 安全 |
PatternMatching(list) | 10 | 2.08 ns | 幾乎與 Count 相同,新的語法 |
Any(list) | 10 | 2.73 ns | 較慢一點,但仍非常快 |
Any(lazy) | 10 | 27.04 ns | 明顯較慢,會開始逐一取得集合中的資料 |
Count(list) | 1,000,000 | 2.03 ns | 一樣快,證明是 O(1) |
Any(list) | 1,000,000 | 2.66 ns | 稍慢,但仍可接受 |
Any(lazy) | 1,000,000 | 26.32 ns | 效能一致但分配記憶體(96 B) |
結論
- 對於 List 或 Array(具體集合):
- Count 和 PatternMatching 表現幾乎相同,都是極快的 O(1)
- Any() 也很快,但略微多一些成本(需進入列舉器)
- 對於 Lazy Enumerable(如 LINQ):
- 只能使用 Any(),但會分配記憶體(如列舉器)
- 效能相較 List 明顯慢,但仍在可接受範圍內(~26ns)
- NullConditional 雖然簡潔,效能略低一點點,但差距不大,可用於強調 null-safety。
VS2022 的 ConsoleApp 專案版本
另外也在 VS2022 裡建立個 ConsoleApp 來執行,大致上程式碼都一樣,不過另外加上設定產出 Markdown 與 CSV 格式的報告

執行時在 Console 裡輸入指令
dotnet run --configuration Release
執行結果

BenchmarkDotNet 的預設輸出資料夾為:
<目前執行的工作目錄>\BenchmarkDotNet.Artifacts\results\
而 Visual Studio 預設的「目前工作目錄」是專案根目錄,所以會在這個路徑看到報告:
Collection_Check_Benchmark\BenchmarkDotNet.Artifacts\results

Markdown Report - default

Markdown Report - Github

Html Report

以上
純粹是在寫興趣的,用寫程式、寫文章來抒解工作壓力