1. 程式人生 > >多角度讓你徹底明白yield語法糖的用法和原理及在C#函數語言程式設計中的作用

多角度讓你徹底明白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,然後return被yield開了光,讓人困惑的地方就是既然方法的返回值是IEnumerable卻在方法體內沒有看到任何實現這個介面的子類,所以第一感覺就是這個yield不簡單,既然程式碼可以跑,那底層肯定幫你實現了一個繼承IEnumerable介面的子類,你說對吧?

2. msdn解釋

有自己的猜想還不行,還得相信權威,看msdn的解釋:https://docs.microsoft.com/zh-cn/dotnet/csharp/language-reference/keywords/yield


如果你在語句中使用 yield 上下文關鍵字,則意味著它在其中出現的方法、運算子或 get 訪問器是迭代器。 通過使用 yield 定義迭代器,可在實現自定義集合型別的 IEnumerator 和 IEnumerable 模式時無需其他顯式類(保留列舉狀態的類,有關示例,請參閱 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是不是非常熟悉?