Linq詳細介紹

来源:http://www.cnblogs.com/pang951189/archive/2017/10/30/7756322.html
-Advertisement-
Play Games

聲明 文檔轉載自:http://www.cnblogs.com/liulun/archive/2013/02/26/2909985.html 在說LINQ之前必須先說說幾個重要的C#語言特性 一:與LINQ有關的語言特性 1.隱式類型 (1)源起 在隱式類型出現之前, 我們在聲明一個變數的時候, 總 ...


聲明----文檔轉載自:http://www.cnblogs.com/liulun/archive/2013/02/26/2909985.html

在說LINQ之前必須先說說幾個重要的C#語言特性

一:與LINQ有關的語言特性

  1.隱式類型

    (1)源起

      在隱式類型出現之前,

      我們在聲明一個變數的時候,

      總是要為一個變數指定他的類型

      甚至在foreach一個集合的時候,

      也要為遍歷的集合的元素,指定變數的類型

      隱式類型的出現,

      程式員就不用再做這個工作了。

    (2)使用方法

       來看下麵的代碼:    

      var a = 1; //int a = 1;
      var b = "123";//string b = "123"; 
      var myObj = new MyObj();//MyObj myObj = new MyObj()

      上面的每行代碼,與每行代碼後面的註釋,起到的作用是完全一樣的

      也就是說,在聲明一個變數(並且同時給它賦值)的時候,完全不用指定變數的類型,只要一個var就解決問題了

    (3)你擔心這樣寫會降低性能嗎?

      我可以負責任的告訴你,這樣寫不會影響性能!

      上面的代碼和註釋里的代碼,編譯後產生的IL代碼(中間語言代碼)是完全一樣的

      (編譯器根據變數的值,推導出變數的類型,才產生的IL代碼)      

    (4)這個關鍵字的好處:

      你不用在聲明一個變數並給這個變數賦值的時候,寫兩次變數類型

      (這一點真的為開發者節省了很多時間)

      在foreach一個集合的時候,可以使用var關鍵字來代替書寫迴圈變數的類型

     (5)註意事項

      你不能用var關鍵字聲明一個變數而不給它賦值

      因為編譯器無法推導出你這個變數是什麼類型的。

  2.匿名類型

    (1)源起

      創建一個對象,一定要先定義這個對象的類型嗎?

      不一定的!

      來看看這段代碼

    (2)使用 

         var obj = new {Guid.Empty, myTitle = "匿名類型", myOtherParam = new int[] { 1, 2, 3, 4 } };
         Console.WriteLine(obj.Empty);//另一個對象的屬性名字,被原封不動的拷貝到匿名對象中來了。
         Console.WriteLine(obj.myTitle);
         Console.ReadKey();

      new關鍵字之後就直接為對象定義了屬性,並且為這些屬性賦值

      而且,對象創建出來之後,在創建對象的方法中,還可以暢通無阻的訪問對象的屬性

      當把一個對象的屬性拷貝到匿名對象中時,可以不用顯示的指定屬性的名字,這時原始屬性的名字會被“拷貝”到匿名對象中

    (3)註意    

      如果你監視變數obj,你會發現,obj的類型是Anonymous Type類型的

      不要試圖在創建匿名對象的方法外面去訪問對象的屬性!

    (4)優點

      這個特性在網站開發中,序列化和反序列化JSON對象時很有用

  3.自動屬性

    (1)源起

      為一個類型定義屬性,我們一般都寫如下的代碼:    

複製代碼
        public class MyObj2
        {
            private Guid _id;
            private string _Title;
            public Guid id 
            {
                get { return _id; }
                set { _id = value; } 
            }
            public string Title
            {
                get { return _Title; }
                set { _Title = value; }
            }
        }
複製代碼

      但很多時候,這些私有變數對我們一點用處也沒有,比如對象關係映射中的實體類。

      自C#3.0引入了自動實現的屬性,

      以上代碼可以寫成如下形式:

    (2)使用

        public class MyObj
        {
            public Guid id { get; set; }
            public string Title { get; set; }
        }

      這個特性也和var關鍵字一樣,是編譯器幫我們做了工作,不會影響性能的

  4.初始化器

    (1)源起

      我們創建一個對象並給對象的屬性賦值,代碼一般寫成下麵的樣子    

            var myObj = new MyObj();
            myObj.id = Guid.NewGuid();
            myObj.Title = "allen";

      自C#3.0引入了對象初始化器,

      代碼可以寫成如下的樣子

    (2)使用    

      var myObj1 = new MyObj() { id = Guid.NewGuid(), Title = "allen" };

      如果一個對象是有參數的構造函數

      那麼代碼看起來就像這樣

      var myObj1 = new MyObj ("allen") { id = Guid.NewGuid(), Title = "allen" };

      集合初始化器的樣例代碼如下:    

      var arr = new List<int>() { 1, 2, 3, 4, 5, 6 };

    (3)優點

      我個人認為:這個特性不是那麼amazing,

      這跟我的編碼習慣有關,集合初始化器也就罷了,

      真的不習慣用對象初始化器初始化一個對象!

  5.委托

    (1)使用

      我們先來看一個簡單的委托代碼    

複製代碼
        delegate Boolean moreOrlessDelgate(int item);
        class Program
        {
            static void Main(string[] args)
            {
                var arr = new List<int>() { 1, 2, 3, 4, 5, 6,7,8 };
                var d1 = new moreOrlessDelgate(More);            
                Print(arr, d1);
                Console.WriteLine("OK");

                var d2 = new moreOrlessDelgate(Less);
                Print(arr, d2);
                Console.WriteLine("OK");
                Console.ReadKey();

            }
            static void Print(List<int> arr,moreOrlessDelgate dl)
            {
                foreach (var item in arr)
                {
                    if (dl(item))
                    {
                        Console.WriteLine(item);
                    }
                }
            }
            static bool More(int item)
            {
                if (item > 3)
                { 
                    return true; 
                }
                return false;
            }
            static bool Less(int item)
            {
                if (item < 3)
                {
                    return true;
                }
                return false;
            }
        }
複製代碼

      這段代碼中

      <1>首先定義了一個委托類型

        delegate Boolean moreOrlessDelgate(int item);

        你看到了,委托和類是一個級別的,確實是這樣:委托是一種類型

        和class標誌的類型不一樣,這種類型代表某一類方法。

        這一句代碼的意思是:moreOrlessDelgate這個類型代表返回值為布爾類型,輸入參數為整形的方法

      <2>有類型就會有類型的實例  

        var d1 = new moreOrlessDelgate(More);     
        var d2 = new moreOrlessDelgate(Less);

        這兩句就是創建moreOrlessDelgate類型實例的代碼,

        它們的輸入參數是兩個方法

      <3>有了類型的實例,就會有操作實例的代碼   

        Print(arr, d1);
        Print(arr, d2);

        我們把前面兩個實例傳遞給了Print方法

        這個方法的第二個參數就是moreOrlessDelgate類型的

        在Print方法內用如下代碼,調用委托類型實例所指向的方法

        dl(item)

  6.泛型

    (1)為什麼要有泛型

      假設你是一個方法的設計者,

      這個方法有一個傳入參數,有一個返回值。

      但你並不知道這個參數和返回值是什麼類型的,

      如果沒有泛型,你可能把參數和返回值的類型都設定為Object了

      那時,你心裡肯定在想:反正一切都是對象,一切的基類都是Object

      沒錯!你是對的!

      這個方法的消費者,會把他的對象傳進來(有可能會做一次裝箱操作)

      並且得到一個Object的返回值,他再把這個返回值強制類型轉化為他需要的類型

      除了裝箱和類型轉化時的性能損耗外,代碼工作的很好!

      那麼這些性能損耗能避免掉嗎?

      有泛型之後就可以了!

    (2)使用

      <1>使用簡單的泛型

        先來看下麵的代碼:        

複製代碼
              var intList = new List<int>() { 1,2,3};
              intList.Add(4);
              intList.Insert(0, 5);
              foreach (var item in intList)
              {
                  Console.WriteLine(item);
              }
              Console.ReadKey();
複製代碼

        在上面這段代碼中我們聲明瞭一個存儲int類型的List容器

        並迴圈列印出了容器里的值

        註意:如果這裡使用Hashtable、Queue或者Stack等非泛型的容器

        就會導致裝箱操作,損耗性能。因為這些容器只能存儲Object類型的數據

      <2>泛型類型

        List<T>、Dictionary<TKey, TValue>等泛型類型都是.net類庫定義好並提供給我們使用的

        但在實際開發中,我們也經常需要定義自己的泛型類型

        來看下麵的代碼:        

複製代碼
          public static class SomethingFactory<T>
          {
              public static T InitInstance(T inObj)
              {
                  if (false)//你的判斷條件
                  {
                      //do what you want...
                      return inObj;
                  }
                  return default(T);
              }
          }
複製代碼

        這段代碼的消費者如下:        

              var a1 = SomethingFactory<int>.InitInstance(12);
              Console.WriteLine(a1);
              Console.ReadKey();

        輸出的結果為0

        這就是一個自定義的靜態泛型類型,

        此類型中的靜態方法InitInstance對傳入的參數做了一個判斷

        如果條件成立,則對傳入參數進行操作之後並把它返回

        如果條件不成立,則返回一個空值

        註意:

          [1]

            傳入參數必須為指定的類型,

            因為我們在使用這個泛型類型的時候,已經規定好它能接收什麼類型的參數

            但在設計這個泛型的時候,我們並不知道使用者將傳遞什麼類型的參數進來

          [2]

            如果你想返回T類型的空值,那麼請用default(T)這種形式

            因為你不知道T是值類型還是引用類型,所以別擅自用null

      <3>泛型約束

        很多時候我們不希望使用者太過自由

        我們希望他們在使用我們設計的泛型類型時

        不要很隨意的傳入任何類型

        對於泛型類型的設計者來說,要求使用者傳入指定的類型是很有必要的

        因為我們只有知道他傳入了什麼東西,才方便對這個東西做操作

        讓我們來給上面設計的泛型類型加一個泛型約束

        代碼如下:        

          public static class SomethingFactory<T> where T:MyObj

        這樣在使用SomethingFactory的時候就只能傳入MyObj類型或MyObj的派生類型啦

        註意:

          還可以寫成這樣

          where T:MyObj,new()

          來約束傳入的類型必須有一個構造函數。        

    (3)泛型的好處

      <1>演算法的重用

        想想看:list類型的排序演算法,對所有類型的list集合都是有用的

      <2>類型安全

      <3>提升性能

        沒有類型轉化了,一方面保證類型安全,另一方面保證性能提升

      <4>可讀性更好

        這一點就不解釋了 

  7.泛型委托

    (1)源起

      委托需要定義delgate類型

      使用起來頗多不便

      而且委托本就代表某一類方法

      開發人員經常使用的委托基本可以歸為三類,

      哪三類呢?

      請看下麵:

    (2)使用

      <1>Predicate泛型委托

        把上面例子中d1和d2賦值的兩行代碼改為如下:    

              //var d1 = new moreOrlessDelgate(More);
              var d1 = new Predicate<int>(More);
              //var d2 = new moreOrlessDelgate(Less);
              var d2 = new Predicate<int>(Less);

        把Print方法的方法簽名改為如下:    

            //static void Print(List<int> arr, moreOrlessDelgate<int> dl)
            static void Print(List<int> arr, Predicate<int> dl)

        然後再運行方法,控制台輸出的結果和原來的結果是一模一樣的。

        那麼Predicate到底是什麼呢?

        來看看他的定義:    

複製代碼
          // 摘要:
          //     表示定義一組條件並確定指定對象是否符合這些條件的方法。
          //
          // 參數:
          //   obj:
          //     要按照由此委托表示的方法中定義的條件進行比較的對象。
          //
          // 類型參數:
          //   T:
          //     要比較的對象的類型。
          //
          // 返回結果:
          //     如果 obj 符合由此委托表示的方法中定義的條件,則為 true;否則為 false。
          public delegate bool Predicate<in T>(T obj);
複製代碼

        看到這個定義,我們大致明白了。

        .net為我們定義了一個委托,

        這個委托表示的方法需要傳入一個T類型的參數,並且需要返回一個bool類型的返回值

        有了它,我們就不用再定義moreOrlessDelgate委托了,

        而且,我們定義的moreOrlessDelgate只能搞int類型的參數,

        Predicate卻不一樣,它可以搞任意類型的參數

        但它規定的還是太死了,它必須有一個返回值,而且必須是布爾類型的,同時,它必須有一個輸入參數

        除了Predicate泛型委托,.net還為我們定義了Action和Func兩個泛型委托

      <2>Action泛型委托

        Action泛型委托限制的就不那麼死了,

        他代表了一類方法:

        可以有0個到16個輸入參數,

        輸入參數的類型是不確定的,

        但不能有返回值,

        來看個例子:      

              var d3 = new Action(noParamNoReturnAction);
              var d4 = new Action<int, string>(twoParamNoReturnAction);

         註意:尖括弧中int和string為方法的輸入參數

複製代碼
            static void noParamNoReturnAction()
            {
                //do what you want
            }
            static void twoParamNoReturnAction(int a, string b)
            {
                //do what you want
            }
複製代碼

       <3>Func泛型委托

        為了彌補Action泛型委托,不能返回值的不足

        .net提供了Func泛型委托,

        相同的是它也是最多0到16個輸入參數,參數類型由使用者確定

        不同的是它規定要有一個返回值,返回值的類型也由使用者確定

        如下示例:      

          var d5 = new Func<int, string>(oneParamOneReturnFunc);

        註意:string類型(最後一個泛型類型)是方法的返回值類型

            static string oneParamOneReturnFunc(int a)
            {
                //do what you want
                return string.Empty;
            }

  8.匿名方法

    (1)源起

      在上面的例子中

      為了得到序列中較大的值

      我們定義了一個More方法      

      var d1 = new Predicate<int>(More);

      然而這個方法,沒有太多邏輯(實際編程過程中,如果邏輯較多,確實應該獨立一個方法出來)

      那麼能不能把More方法中的邏輯,直接寫出來呢?

      C#2.0之後就可以了,

      請看下麵的代碼:

    (2)使用      

複製代碼
            var arr = new List<int>() { 1, 2, 3, 4, 5, 6, 7, 8 };
            //var d1 = new moreOrlessDelgate(More);
            //var d1 = new Predicate<int>(More);
            var d1 = new Predicate<int>(delegate(int item)
            {

          //可以訪問當前上下文中的變數
          Console.WriteLine(arr.Count);

                if (item > 3)

                {
                    return true;
                }
                return false;
            });
            Print(arr, d1);
            Console.WriteLine("OK");
複製代碼

      我們傳遞了一個代碼塊給Predicate的構造函數

      其實這個代碼塊就是More函數的邏輯

    (3)好處

      <1>代碼可讀性更好

      <2>可以訪問當前上下文中的變數

        這個用處非常大,

        如果我們仍舊用原來的More函數

        想要訪問arr變數,勢必要把arr寫成類級別的私有變數了

        用匿名函數的話,就不用這麼做了。

  9.Lambda表達式

    (1)源起

      .net的設計者發現在使用匿名方法時,

      仍舊有一些多餘的字母或單詞的編碼工作

      比如delegate關鍵字

      於是進一步簡化了匿名方法的寫法

    (2)使用      

            List<int> arr = new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
            arr.ForEach(new Action<int>(delegate(int a) { Console.WriteLine(a); }));
            arr.ForEach(new Action<int>(a => Console.WriteLine(a)));

       匿名方法的代碼如下:

      delegate(int a) { Console.WriteLine(a); }

      使用lambda表達式的代碼如下:

      a => Console.WriteLine(a)

      這裡解釋一下這個lambda表達式

      <1>

        a是輸入參數,編譯器可以自動推斷出它是什麼類型的,

        如果沒有輸入參數,可以寫成這樣:

        () => Console.WriteLine("ddd")

      <2>

        =>是lambda操作符

      <3>

        Console.WriteLine(a)是要執行的語句。

        如果是多條語句的話,可以用{}包起來。

        如果需要返回值的話,可以直接寫return語句

  10.擴展方法

    (1)源起

      如果想給一個類型增加行為,一定要通過繼承的方式實現嗎?

      不一定的!

    (2)使用

      來看看這段代碼:    

          public static void PrintString(this String val)
          {
              Console.WriteLine(val);
          }

      消費這段代碼的代碼如下:    

            var a = "aaa";
            a.PrintString();
            Console.ReadKey();

      我想你看到擴展方法的威力了。

      本來string類型沒有PrintString方法

      但通過我們上面的代碼,就給string類型"擴展"了一個PrintString方法

      (1)先決條件

        <1>擴展方法必須在一個非嵌套、非泛型的靜態類中定義

        <2>擴展方法必須是一個靜態方法

        <3>擴展方法至少要有一個參數

        <4>第一個參數必須附加this關鍵字作為首碼

        <5>第一個參數不能有其他修飾符(比如ref或者out)

        <6>第一個參數不能是指針類型

      (2)註意事項

        <1>跟前面提到的幾個特性一樣,擴展方法只會增加編譯器的工作,不會影響性能(用繼承的方式為一個類型增加特性反而會影響性能)

        <2>如果原來的類中有一個方法,跟你的擴展方法一樣(至少用起來是一樣),那麼你的擴展方法獎不會被調用,編譯器也不會提示你

        <3>擴展方法太強大了,會影響架構、模式、可讀性等等等等....

  11.迭代器

  ·  (1)使用

      我們每次針對集合類型編寫foreach代碼塊,都是在使用迭代器

      這些集合類型都實現了IEnumerable介面

      都有一個GetEnumerator方法

      但對於數組類型就不是這樣

      編譯器把針對數組類型的foreach代碼塊

      替換成了for代碼塊。

      來看看List的類型簽名:    

      public class List<T> : IList<T>, ICollection<T>, IEnumerable<T>, IList, ICollection, IEnumerable

      IEnumerable介面,只定義了一個方法就是:    

      IEnumerator<T> GetEnumerator();

    (2)迭代器的優點:

      假設我們需要遍歷一個龐大的集合

      只要集合中的某一個元素滿足條件

      就完成了任務

      你認為需要把這個龐大的集合全部載入到記憶體中來嗎?

      當然不用(C#3.0之後就不用了)!

      來看看這段代碼:      

複製代碼
        static IEnumerable<int> GetIterator()
        {
            Console.WriteLine("迭代器返回了1");
            yield return 1;
            Console.WriteLine("迭代器返回了2");
            yield return 2;
            Console.WriteLine("迭代器返回了3");
            yield return 3;
        }
複製代碼

      消費這個函數的代碼如下:      

複製代碼
            foreach (var i in GetIterator())
            {
                if (i == 2)
                {
                    break;
                }
                Console.WriteLine(i);
            }
            Console.ReadKey();
複製代碼

      輸出結果為:      

      迭代器返回了1
      1
      迭代器返回了2

      大家可以看到:

      當迭代器返回2之後,foreach就退出了

      並沒有輸出“迭代器返回了3”

      也就是說下麵的工作沒有做。

    (3)yield 關鍵字

      MSDN中的解釋如下:

      在迭代器塊中用於向枚舉數對象提供值或發出迭代結束信號。

      也就是說,我們可以在生成迭代器的時候,來確定什麼時候終結迭代邏輯

      上面的代碼可以改成如下形式:      

複製代碼
          static IEnumerable<int> GetIterator()
          {
              Console.WriteLine("迭代器返回了1");
              yield return 1;
              Console.WriteLine("迭代器返回了2");
              yield break;
              Console.WriteLine("迭代器返回了3");
              yield return 3;
          }
複製代碼

     (4)註意事項

      <1>做foreach迴圈時多考慮線程安全性      

        在foreach時不要試圖對被遍歷的集合進行remove和add等操作

        任何集合,即使被標記為線程安全的,在foreach的時候,增加項和移除項的操作都會導致異常

        (我在這裡犯過錯)

      <2>IEnumerable介面是LINQ特性的核心介面

        只有實現了IEnumerable介面的集合

        才能執行相關的LINQ操作,比如select,where等

        這些操作,我們接下來會講到。

二:LINQ

  1.查詢操作符

    (1)源起

      .net的設計者在類庫中定義了一系列的擴展方法

      來方便用戶操作集合對象

      這些擴展方法構成了LINQ的查詢操作符

    (2)使用

      這一系列的擴展方法,比如:

      Where,Max,Select,Sum,Any,Average,All,Concat等

      都是針對IEnumerable的對象進行擴展的

      也就是說,只要實現了IEnumerable介面,就可以使用這些擴展方法

      來看看這段代碼:      

            List<int> arr = new List<int>() { 1, 2, 3, 4, 5, 6, 7 };
            var result = arr.Where(a => { return a > 3; }).Sum();
            Console.WriteLine(result);
            Console.ReadKey();

      這段代碼中,用到了兩個擴展方法。

      <1>

        Where擴展方法,需要傳入一個Func<int,bool>類型的泛型委托

        這個泛型委托,需要一個int類型的輸入參數和一個布爾類型的返回值

        我們直接把a => { return a > 3; }這個lambda表達式傳遞給了Where方法

        a就是int類型的輸入參數,返回a是否大於3的結果。

      <2>

        Sum擴展方法計算了Where擴展方法返回的集合的和。

    (3)好處

      上面的代碼中

      arr.Where(a => { return a > 3; }).Sum();

      這一句完全可以寫成如下代碼:

      (from v in arr where v > 3 select v).Sum();

      而且兩句代碼的執行細節是完全一樣的

      大家可以看到,第二句代碼更符合語義,更容易讀懂

      第二句代碼中的where,就是我們要說的查詢操作符。

    (4)標準查詢操作符說明

      <1>過濾

        Where

        用法:arr.Where(a => { return a > 3; })

        說明:找到集合中滿足指定條件的元素

        OfType

        用法:arr.OfType<int>()

        說明:根據指定類型,篩選集合中的元素

      <2>投影

        Select

        用法:arr.Select<int, string>(a => a.ToString());

        說明:將集合中的每個元素投影的新集合中。上例中:新集合是一個IEnumerable<String>的集合

        SelectMany

        用法:arr.SelectMany<int, string>(a => { return new List<string>() { "a", a.ToString() }; });

        說明:將序列的每個元素投影到一個序列中,最終把所有的序列合併

      <3>還有很多查詢操作符,請翻MSDN,以後有時間我將另起一篇文章把這些操作符寫全。      

  2.查詢表達式

    (1)源起

      上面我們已經提到,使用查詢操作符表示的擴展方法來操作集合;

      雖然已經很方便了,但在可讀性和代碼的語義來考慮,仍有不足;

      於是就產生了查詢表達式的寫法。

      雖然這很像SQL語句,但他們卻有著本質的不同。

    (2)用法

      from v in arr where v > 3 select v

      這就是一個非常簡單的查詢表達式

    (3)說明:

      先看一段偽代碼:      

      from [type] id in source
      [join [type] id in source on expr equals expr [into subGroup]]
      [from [type] id in source | let id = expr | where condition]
      [orderby ordering,ordering,ordering...]
      select expr | group expr by key
      [into id query]

      <1>第一行的解釋:

        type是可選的,

        id是集合中的一項,

        source是一個集合,

        如果集合中的類型與type指定的類型不同則導致強制類型轉化

      <2>第二行的解釋:        

        一個查詢表達式中可以有0個或多個join子句,

        這裡的source可以是一個全新的集合,可以不等於第一句中的source

        expr可以是一個表達式

        [into subGroup] subGroup是一個中間變數,

        它繼承自IGrouping,代表一個分組,也就是說“一對多”里的“多”

        可以通過這個變數得到這一組包含的對象個數,以及這一組對象的鍵

        比如:        

複製代碼
        from c in db.Customers
            join o in db.Orders on c.CustomerID
            equals o.CustomerID into orders
            select new
            {
                c.ContactName,
                OrderCount = orders.Count()
            };
複製代碼

      <3>第三行的解釋:     

        一個查詢表達式中可以有1個或多個from子句

        一個查詢表達式中可以有0個或多個let子句,let子句可以創建一個臨時變數

        比如:        

            from u in users
             let number = Int32.Parse(u.Username.Substring(u.Username.Length - 1))
             where u.ID < 9 && number % 2 == 0
             select u

        一個查詢表達式中可以有0個或多個where子句,where子句可以指定查詢條件

      <4>第四行的解釋:

        一個查詢表達式可以有0個或多個排序方式

        每個排序方式以逗號分割

      <5>第五行的解釋:

        一個查詢表達式必須以select或者group by結束

        select後跟要檢索的內容

        group by 是對檢索的內容進行分組

        比如:        

            from p in db.Products  
            group p by p.CategoryID into g  
            select new {  g.Key, NumProducts = g.Count()}; 

      <6>第六行的解釋:

        最後一個into子句起到的作用是

        將前面語句的結果作為後面語句操作的數據源

        比如:        

複製代碼
            from p in db.Employees
             select new
             {
                 LastName = p.LastName,
                 TitleOfCourtesy = p.TitleOfCourtesy
             } into EmployeesList
             orderby EmployeesList.TitleOfCourtesy ascending
             select EmployeesList;
複製代碼

三:參考資料

  《LINQ實戰》

  《深入理解C#》第二版

  《CLR VIA C#》第三版

  《C# 高級編程》第四版

  還有很多網路上的文章,就不一一例舉了

四:修改記錄

  1.2013-02-12夜

    (1)完成了第一部分的大多數內容

    (2)修改了文章的排版

    (3)通讀了第一部分,修改了一些讀起來不通順的語句,修改了錯別字

  2.2013-02-26夜

    (1)完成了第二部分的內容

    (2)刪掉了表達式樹的內容【文章篇幅實在太長了】

    (3)完善了第一部分的內容

  2.2013-02-27晨

    (1)修改了一些錯別字

  3.2017-03-02午後

    (1)修改了幾個錯別字,幾個標點符號


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 一、服務分類 1、二進位包 (1)、 快速安裝、卸載、升級和管理軟體 (2)、安裝簡潔、速度快 (3)、經過封裝,無法直接獲取源代碼 (4)、功能選擇性差,功能定製不靈活 2、源碼包 (1)、獲得最新的軟體版本,及時修複bug (2)、根據用戶需要,靈活定製軟體功能 二、RPM包的服務管理 RPM包 ...
  • 安裝: tar -zxvf keepalived-1.2.2.tar.gz yum list all |grep "ipvsadm" yum -y install kernel-devel openssl-develpopt-devel ipvsadm libnl libnl-devel servi ...
  • 演示產品近乎下載地址:http://www.jinhusns.com/Products/Download ...
  • 原文:https://www.stevejgordon.co.uk/asp-net-core-anatomy-part-3-addmvc 發佈於:2017年4月環境:ASP.NET Core 1.1 本系列前面兩篇文章介紹了ASP.NET Core中IServiceCollection兩個主要擴展方 ...
  • 文件拖拽: 效果:將一個文件拖拽到窗體的某個控制項時,將該控制項的路徑顯示在該控制項上,只要拿到了路徑自然可以讀取文件中的內容了。 將一個控制項的屬性AllowDrop設置為true,然後添加DragDrop、DragEnter時間處理函數,如下: 圖片的縮放和拖拽: 一、實現滑鼠滾輪控製圖片縮放; 1、設 ...
  • 有時設計RDLC報表時,我們會少不了在報表呈現圖片。今天花上些少時間來實現它們:你可以在設計RDLC報表時,找到Report Data下的Image,按Mouse右鍵,出現Add Image...,如下: 選擇圖片: 接下來,我們就可以把添加的圖片,作為一個報表對象添加至報表。 在添加圖片時,有好幾 ...
  • 線程: 每個Windows進程都有用於進入程式的進入點(entry point)的主線程(Main Thread),例如.Net framework執行程式(控制台、Windows WPF等應用程式)使用Main()方法作為程式的進入點,調用該方法時會自動建立主線程。 線程是Windows進程中的獨 ...
  • 問題描述 主要解決DataTable數據轉化為JSON,從Controller傳遞數據給View的問題。 1 內容區 1 內容區 提供如下方法,僅供參考 2 版權區 2 版權區 感謝您的閱讀,若有不足之處,歡迎指教,共同學習、共同進步。 博主網址:http://www.cnblogs.com/wan ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...