C# 笨問題 - 唯讀的 ICollection 屬性為什麼能設定初始值?
5 |
一時糊塗,被一段程式迷惑。
Foo 類別的 ICollection<string> 屬性設成 { get; } 唯讀,嘗試將 Items 設成某個 List<string> 編譯出錯完全合理,但為什麼用 Items = { "Item3", "Item4" } 指定初始值卻可以過關?
void Main()
{
var list = new List<string> {
"Item1", "Item2"
};
var foo1 = new Foo {
// Compiler Erro: cannot be assigned to -- it is read only
Items = list
};
var foo2 = new Foo
{
Items = {
"Item3", "Item4"
}
};
}
public class Foo
{
public ICollection<string> Items { get; } = new List<string>();
}
RTFM,本草綱目有記載,Collection Initializer 可用來初始化有實作 IEnumerable 且有 Add() 方法的集合屬性,取代反覆呼叫多次加入初值。支援的 Add() 有兩種:Add(TValue) 及 Add(TKey, TValue),也就是我們平常可以用它指定 List<T>、T[]、Dictionary<K,V> 初始值的原因。
換言之,Collection Initializer 用 = 指派內容,背後是靠反覆呼叫 Add 方法,而非覆寫屬性內容。如此便能解釋為什麼 ICollection<string> Items { get; } 雖是唯讀屬性,C# 背後用 Items.Add("Item3")、Items.Add("Item4") 仍可完成初始值設定。
用 LINQPad 的 IL Code 反組譯功能證明!
結案。
Explanation of why a get only ICollection property cannot be assigned but can be initialized by collection initializer.
Comments
# by 理科羊
Add會有Array.Copy與Capacity * 2 (default =4)的效能問題..... 在大陣列的情況下還是自己宣告好長度會比較安全,我被這個雷了好幾次
# by Jeffrey
to 理科羊,誠心發問:是多大的陣列規模遇到問題?
# by 理科羊
[Benchmark] public void TestList() { var ints = new List<int>(); for (var i = 0; i < maxRun; i++) { ints.Add(i); } } [Benchmark] public void TestListAndInit() { var ints = new List<int>(maxRun); for (var i = 0; i < maxRun; i++) { ints.Add(i); } } [Benchmark] public void TestArray() { var ints = new int[maxRun]; for (var i = 0; i < maxRun; i++) { ints[i] = i; } } BenchmarkDotNet=v0.13.2, OS=Windows 10 (10.0.19045.2546) AMD Ryzen 7 PRO 5850U with Radeon Graphics, 1 CPU, 16 logical and 8 physical cores .NET SDK=7.0.102 [Host] : .NET 6.0.13 (6.0.1322.58009), X64 RyuJIT AVX2 [AttachedDebugger] DefaultJob : .NET 6.0.13 (6.0.1322.58009), X64 RyuJIT AVX2 | Method | Mean | Error | StdDev | Gen0 | Gen1 | Gen2 | Allocated | |---------------- |---------:|--------:|--------:|---------:|---------:|---------:|-----------:| | TestList | 337.7 us | 6.69 us | 5.93 us | 285.6445 | 285.6445 | 285.6445 | 1024.48 KB | | TestListAndInit | 245.5 us | 4.90 us | 9.44 us | 124.5117 | 124.5117 | 124.5117 | 390.72 KB | | TestArray | 152.5 us | 2.42 us | 3.98 us | 124.7559 | 124.7559 | 124.7559 | 390.69 KB | 本來想寫blog貼連結過來比較不會麻煩,但是想想好像不太好,就直接貼code了,我直接用Benchmark跑出的結果
# by 理科羊
https://i.imgur.com/REUm3mk.png https://i.imgur.com/RMPSJoy.png 轉成圖片應該會比較好看
# by Jeffrey
to 理科羊,感謝分享。寫成 Blog 貼連結是好點子,推!