多角度讓你徹底明白yield語法糖的用法和原理及在C#函數語言程式設計中的作用
如果大家讀過dapper原始碼,你會發現這內部有很多方法都用到了yield關鍵詞,那yield到底是用來幹嘛的,能不能拿掉,拿掉與不拿掉有多大的差別,首先上一段dapper中精簡後的Query方法,先讓大家眼見為實。
private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType) { object param = command.Parameters; var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param?.GetType()); var info = GetCacheInfo(identity, param, command.AddToCache); IDbCommand cmd = null; IDataReader reader = null; bool wasClosed = cnn.State == ConnectionState.Closed; try { while (reader.Read()) { object val = func(reader); if (val == null || val is T) { yield return (T)val; } else { yield return (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture); } } } }
一:yield探究
1. 骨架程式碼猜想
骨架程式碼其實很簡單,方法的返回值是IEnumerable
2. msdn解釋
有自己的猜想還不行,還得相信權威,看msdn的解釋:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/yield
如果你在語句中使用 yield 上下文關鍵字,則意味著它在其中出現的方法、運算子或 get 訪問器是迭代器。 通過使用 yield 定義迭代器,可在實現自定義集合型別的 IEnumerator
沒用過yield之前,看這句話肯定是一頭霧水,只有在業務開發中踩過坑,才能體會到yield所帶來的快感。
3. 從IL入手
為了方便探究原理,我來寫一個不能再簡單的例子。
public static void Main(string[] args) { var list = GetList(new int[] { 1, 2, 3, 4, 5 }); } public static IEnumerable<int> GetList(int[] nums) { foreach (var num in nums) { yield return num; } }
對,就是這麼簡單,接下來用ILSpy反編譯開啟這其中的神祕面紗。
從截圖中看最讓人好奇的有兩點。
<1> 無緣無故的多了一個叫做<GetList>d__1 類
好奇心驅使著我看一下這個類到底都有些什麼?由於IL程式碼太多,我做一下精簡,從下面的IL程式碼中可以發現,果然是實現了IEnumerable介面,如果你瞭解設計模式中的迭代器模式,那這裡的MoveNext,Current是不是非常熟悉?