国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

Linq研究

2019-11-17 02:40:53
字體:
來源:轉載
供稿:網友

Linq研究

微軟在.NET 3.5中加入了LINQ技術,作為配套改進了C#語言,加入了Lambda表達式,擴展方法,匿名類型等新特性用以支持LINQ。微軟同時提出了要使用聲明式編程,即描述計算規則,而不是描述計算過程。

使用LINQ技術能很好地做到聲明式編程,寫出的代碼表意能力強,可讀性高,避免了以往或其他語言的代碼中充斥大量表意不明的for循環甚至多層循環的問題。不要小看for循環和Where,Select,OrderBy等擴展方法的區別,可以不通過注釋一眼就能看出代碼意圖真的很重要。當看到java代碼中一大堆的for循環,包括多層循環,又沒有注釋,必須仔細看才能了解代碼作用時,真的很頭大。個人認為LINQ是C#語言區別于其他語言的最顯著的特性,也是最大的優勢之一。

當然現在大多數主流語言都加入了Lambda表達式,從而可以使用類似于LINQ的技術,達到聲明式編程。比如Java語言在Java 8中加入了和C#幾乎一樣的Lambda表達式語法,并加入了Stream API,以達到類似于LINQ的用法。

如此可見,聲明式編程是發展趨勢,既然使用C#,就要多用LINQ,用好LINQ,用對LINQ。不要再寫一堆一堆的for循環了!

要用好LINQ,就要學好LINQ,理解其原理,機制和用法。推薦一個學習和研究LINQ的好工具LINQPad,下面是官網和官網上的截圖。

http://www.linqpad.net/

Image

下面針對幾個關鍵點,對LINQ進行一些初步研究。有些問題可能是使用LINQ多年的人都理解得不對的。

首先看下面的程序。

using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.Threading.Tasks;namespace LinqResearch{    class PRogram    {        static void Main(string[] args)        {            var list = new List<int> { 2, 1, 6, 4, 3, 5, 7, 8, 10, 9 };            Console.WriteLine("list");            var list1 = list.Select(i =>            {                Console.WriteLine("In select {0}", i);                return i * i;            });            Console.WriteLine("list1");            var list2 = list1.Where(i =>            {                Console.WriteLine("In where>10 {0}", i);                return i > 10;            });            Console.WriteLine("list2");            var list3 = list2.Where(i =>            {                Console.WriteLine("In where<60 {0}", i);                return i < 60;            });            Console.WriteLine("list3");            var list4 = list3.OrderBy(i =>            {                Console.WriteLine("In orderby {0}", i);                return i;            });            Console.WriteLine("list4");            var list5 = list4.ToList();            Console.WriteLine("list5");            foreach (var i in list5)            {                Console.WriteLine(i);            }        }    }}

先不要看下面的運行結果,想想打印出的是什么,然后再看結果,看看和想的一樣嗎?

listlist1list2list3list4In select 2In where>10 4In select 1In where>10 1In select 6In where>10 36In where<60 36In select 4In where>10 16In where<60 16In select 3In where>10 9In select 5In where>10 25In where<60 25In select 7In where>10 49In where<60 49In select 8In where>10 64In where<60 64In select 10In where>10 100In where<60 100In select 9In where>10 81In where<60 81In orderby 36In orderby 16In orderby 25In orderby 49list516253649

為什么先打印出list 到 list4,而沒有進到Lambda里面?

這是因為LINQ是延時計算的,即只有foreach或ToList時才去做真正計算,前面的Select,Where等語句只是聲明了計算規則,而不進行計算。

這點很重要,如果不明白這點,就會寫出有BUG的代碼,如下面的程序,打印出的是1和2,而不是1。

            var a = 2;            var list = new List<int> { 1, 2, 3 };            var list1 = list.Where(i => i < a);            a = 3;            foreach (var i in list1)            {                Console.WriteLine(i);            }

后面打印出的為什么先是select和where交錯,然后是orderby,而不是先select再where,最后orderby?

這時因為Select,Where等這些擴展方法,在聲明計算規則時是有優化的(內部可能通過表達式樹等方法實現),它并不是傻傻的按照原始定義的規則,順序執行,而是以一種優化的方法計算并獲得結果。所以使用 LINQ一般會比自己寫的原始的一大堆for循環性能還高,除非花大量時間優化自己的邏輯(一般不會有這個時間)。

可以看到針對元素2和1,并沒有打印出In where<60 的行,這說明針對這兩個元素,第二個Where里的代碼并沒有執行,因為第一個Where都沒有通過。在進行完投影(Select)和篩選(Where)后,最后進行排序(OrderBy),只針對篩選后留下的元素執行OrderBy里面的計算邏輯,一點也不浪費。

上面的程序有人可能會寫成這樣。

            var list = new List<int> { 2, 1, 6, 4, 3, 5, 7, 8, 10, 9 };            Console.WriteLine("list");            var list1 = list.Select(i =>            {                Console.WriteLine("In select {0}", i);                return i * i;            }).ToList();            Console.WriteLine("list1");            var list2 = list1.Where(i =>            {                Console.WriteLine("In where>10 {0}", i);                return i > 10;            }).ToList();            Console.WriteLine("list2");            var list3 = list2.Where(i =>            {                Console.WriteLine("In where<60 {0}", i);                return i < 60;            }).ToList();            Console.WriteLine("list3");            var list4 = list3.OrderBy(i =>            {                Console.WriteLine("In orderby {0}", i);                return i;            }).ToList();            Console.WriteLine("list4");            var list5 = list4.ToList();            Console.WriteLine("list5");            foreach (var i in list5)            {                Console.WriteLine(i);            }

這樣寫打印出的結果為,

listIn select 2In select 1In select 6In select 4In select 3In select 5In select 7In select 8In select 10In select 9list1In where>10 4In where>10 1In where>10 36In where>10 16In where>10 9In where>10 25In where>10 49In where>10 64In where>10 100In where>10 81list2In where<60 36In where<60 16In where<60 25In where<60 49In where<60 64In where<60 100In where<60 81list3In orderby 36In orderby 16In orderby 25In orderby 49list4list516253649

雖然也能得到正確的結果,但是卻是不合理的。因為這樣寫每步都執行計算,并放到集合中,會有很大的性能損耗,失去了使用LINQ的優勢。

何時進行真正計算是個值得思考的問題,多了會增加中間集合的數量,性能不好,少了有可能會有多次重復計算,性能也不好。下文會有說明。

如果使用Resharper插件,會提示出重復迭代(可能會有多次重復計算)的地方,這個功能很好,便于大家分析是否存在問題。

使用Max和Min要小心,Max和Min等聚合運算需要集合中存在值,否則會拋出異常,筆者多次遇到這個問題產生的BUG。

當前面有Where篩選時,后面使用Max或Min不一定是安全的,如下面的代碼會拋出異常。

            var a = 0;            var list = new List<int> { 1, 2, 3 };            var min = list.Where(i => i < a).Min();            Console.WriteLine(min);

如果a來源于外部值,又有大段的邏輯,這樣的BUG不易發現。

解決方法有多種,我們來分析一下,一種方法是可以先調一下Any,再使用Min,代碼如下,

            var a = 0;            var list = new List<int> { 1, 2, 3 };            var list2 = list.Where(i => i < a);            var min = 0;            if (list2.Any())            {                min = list2.Min();            }            Console.WriteLine(min);

把代碼改為如下,

            var a = 3;            var list = new List<int> { 1, 2, 3 };            var list2 = list.Where(i =>            {                Console.WriteLine("In where {0}", i);                return i < a;            });            var min = 0;            if (list2.Any(i =>            {                Console.WriteLine("In any {0}", i);                return true;            }))            {                min = list2.Min();            }            Console
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 沛县| 永福县| 禄劝| 舒兰市| 乌拉特中旗| 陇川县| 宁都县| 连州市| 西丰县| 栾川县| 黑山县| 林州市| 清水河县| 阳春市| 襄汾县| 怀宁县| 诏安县| 石嘴山市| 山丹县| 四会市| 湾仔区| 抚远县| 宁陵县| 靖安县| 化德县| 眉山市| 驻马店市| 英德市| 呼和浩特市| 依兰县| 土默特左旗| 通河县| 长沙市| 祁门县| 东兰县| 湛江市| 聂拉木县| 壶关县| 大荔县| 资源县| 平阳县|