重新認識 C# [2] - C# 2.0 Iterator 及其他進化
| | 0 | | ![]() |
【本系列是我的 C# in Depth 第四版讀書筆記,背景故事在這裡】
繼續談 C# 2.0
Iterator
- foreach 在 IEnumerable 之外多支援 IEnumerable<T>,並加入了 Iterator
- Iterator 可用於 IEnumerable、IEnumerable<T>、IEnumerator、IEnumerator<T>,Iterator Block 可使用 yield return 及 yield return 實現依序傳回資料,IEnumerable 傳回 object 型別,IEnumerable<T> 則為 T 型別
註:這就是為什麼 foreach(var m in Regex.Matches(...)) 無法存取 m.Value 的原因,Matches 傳回的 MatchCollection 只有實作 IEnumerable,傳回型別為 object,需寫成 foreach (Match m in Regex.Matches(...)) 明確定義型別;如要結合 LINQ 則需 Cast 轉成 IEnumerable<Match>。參考:小技巧 - 對只支援 foreach 的集合執行 LINQ 動作 - Lazy Execution、Lazy Evaluation
跑迴圈時一次傳回一點,不必一次把全部結果算出來存進記憶體,效率好也省 RAM,延伸閱讀:善用 yield return 省時省 CPU 省 RAM,打造高效率程式
static IEnumerable<int> CreateSimpleIterator()
{ // 以下區域為 Interator Block,GetEnumerator() 時不會馬上執行
yield return 10; //直到第一次 MoveNext() 才會執行這行
for (int i = 0; i < 3; i++)
{
yield return i; // 之後每次 MoveNext() 執行這裡
}
yield return 20;
}
IEnumerable<int> enumerable = CreateSimpleIterator();
using (IEnumerator<int> enumerator =
enumerable.GetEnumerator())
{
while (enumerator.MoveNext())
{
int value = enumerator.Current;
Console.WriteLine(value);
}
}
- 在 Iterator Block 放 finally 會怎樣?
static IEnumerable<string> Iterator()
{
try
{
Console.WriteLine("Before first yield");
yield return "first"; //想像成 yield return 後暫停在這裡
Console.WriteLine("Between yields");
yield return "second"; //yield return 後暫停在這裡
Console.WriteLine("After second yield");
}
finally
{
// 結束 foreach 時才執行這裡
Console.WriteLine("In finally block");
}
}
- Iterator Block 如果有用到 Unmanaged 資源(例如:讀取檔案),記得用 using 隱含強制呼叫 IDispose,foreach 呼叫過程即使出錯也要釋放資源。
- Iterator Block 在編譯時會被轉成一個實作狀態機 Pattern 的類別,有點小複雜,有興趣請看書或參考安德魯這篇:How yield return Work ?
C# 2.0 的其他小改進
- Partial Types
將同一類別、Struct、介面的部分寫在多個 .cs,很常搭配程式產生器使用,在程式自動產生 .cs 外再新增一個個 .cs 寫自訂邏輯。 - Static Class
強制類別不能建立 Instance,搭配 C# 3.0 的擴充方法使用 - Property 的 set/get 可以設不同存取等級
private string _text;
public string Test {
get { return _text; }
private set { _text = value; }
}
- Namespace 別名
using WinForms = System.Windows.Forms;
using WebForms = System.Web.UI.WebControls;
//另外 WinForms.Button 可以寫成 WinForms::Button,防止有類別叫 WinForms
//global::System.DateTime 常用於程式碼產生器避免撞名
- External Alias 來自不同組件的同 Namespace 同名型別
extern alias JsonNet;
extern alias JsonNetAlternative;
using JsonNet::Newtonsoft.Json.Linq;
using AltJObject = JsonNetAlternative::Newtonsoft.Json.Linq.JObject;
...
JObject obj = new JObject();
AltJObject alt = new AltJObject();
- Pragma Directive
#pragma warning disable CS0219 //停用 variable is assigned but its value is never used 警告
int variable = CallSomeMethod();
#pragma warning restore CS0219
- Fixed-Size Buffer (適用 Unsafe 程式,跟 C/C++ 世界溝通可用)
unsafe struct VersionedData
{
public int Major;
public int Minor;
public fixed byte Data[16];
}
- InternalsVisibleTo 單元測試時很好用。延伸閱讀:【笨問題】紅杏出牆的internal類別
My notes for C# in Depth part 3
Comments
Be the first to post a comment