從擴展方法到匿名方法再到LINQ


   今天下午依舊逃課(大學生就業指導)很操蛋的課程,要是上這課真能給我們指條路也就好了,看着男同學們一個個陽痿早泄,女同學們一個個搔首弄姿,哈哈還是不去了,所以兩年來此類的課程一節都沒上過,於是就寫點東西,此外的可以暫時忽略。

1.首先我們應該知道什么是擴展方法

擴展方法使您能夠向現有類型“添加”方法,而無需創建新的派生類型、重新編譯或以其他方式修改原始類型。
擴展方法是一種特殊的靜態方法,但可以像擴展類型上的實例方法一樣進行調用。
2.知道了擴展方法怎么創建呢?
  1. 定義一個靜態類以包含擴展方法。 
  2. 將該擴展方法實現為靜態方法,並使其至少具有與包含類相同的可見性。
  3. 該方法的第一個參數指定方法所操作的類型;該參數必須以 this 修飾符開頭。
  4. 在調用代碼中,添加一條 using 指令以指定包含擴展方法類的 命名空間
  5. 按照與調用類型上的實例方法一樣的方式調用擴展方法。

 好了,概念性的問題我們都知道了,如果你還沒有理解,我們直接寫個簡單的實例:

using System;

namespace 擴展方法
{
    using StringUnit;//添加包含擴展方法類的命名空間
    class Program
    {
        static void Main(string[] args)
        {
           string s1 = "你好";
           string s2 = "rohelm!";
           string s3 = "["+s1.StringCombine(s2).StringCombine("]");
           Console.WriteLine(s3);
           string s4 = StringPro.StringQuote(s2);//也可以利用靜態方法調用哦
           string s5 = s1.StringQuote();
           Console.WriteLine(s4.StringCombine(s5));
           Console.ReadKey();
        }
    }
}
namespace StringUnit
{
    static class StringPro//定義一個靜態類以包含擴展方法
    {
        public static string StringCombine(this string str1, string str2)//第一個參數必須以 this修飾符開頭。 
        {
            return str1 + str2;
        }
        public static string StringQuote(this string str)
        {
            return "[" + str + "]";
        }
    }
}

 我們從上面的代碼中隨意的取出一個進行大概的說明:public static string StringQuote(this string str),第一個參數不是由調用代碼指定的,因為它表示正應用運算符的類型,並且編譯器已經知道對象的類型。也就是這個擴展方法只適用於string類型的運算,因此當我們在Main方法中
s2.StringQuote 當我們一點的時候StringQuote就會自動奔出來,好像是String類內置的一個方法一樣,我們於是可以按照實例方法一樣的方式調用擴展方法,當然我們依然可以使用靜態方法一樣的方式調用擴展方法例如:StringPro.StringQuote(s2)。
這里我們要注意擴展方法和類的static以及this關鍵字.

好了我們現在寫一個大家最簡單的匿名方法,知道我要干什么嗎?先寫出來。。。

class Program
    {
        static void Main(string[] args)
        {
         StringDelegate f=delegate(string s1,string s2){
         return s1+s2;
         };
         Console.WriteLine(f("你好","rohelm.X"));
           Console.ReadKey();
        }
    }
    delegate string StringDelegate(string s1,string s2);

簡單的都不好意思解釋,好了現在我們簡化匿名函數

 StringDelegate f=delegate(string s1,string s2){
         return s1+s2;
};

於是乎這句變得非常怪異而簡單:

StringDelegate f=(s1,s2)=>{
         return s1+s2;
         };

這就是使用Linq和lambda表達式得出的結果,為什么我們不在需要指出s1,s2的參數類型呢,你覺得編譯器知道他們該是什么類型嗎?

當然,這就是因為我們這個匿名函數是由delegate string StringDelegate(string s1,string s2); 這個委托約束的,因此當然不用猜測就知道

s1,s2的數據類型了,這叫類型推斷,所以省略了似乎多余的參數類型聲明,但是或許對於新手損失可讀性,但是他的確非常強大。其實這個例子很簡單,就是參數類表的優化而已,后面{}內仍舊是方法體。

話到這里我們開始新手的Linq之旅

LINQ是.NET3.5引入的功能,LINQ 通過提供一種跨各種數據源和數據格式使用數據的一致模型,簡化了這一情況。

目的:以統一的方式對數據進行操作。

Linq和擴展方法又有什么聯系?好了我們看一下一個簡單的示例:

 

 1  int[] numbers = new int[7] { 60, 17, 12, 63, 35, 5, 6 };
 2         IEnumerable<int> query = numbers.OrderByDescending(i=>i);//按照降序排列
 3         foreach (var i in query)
 4         {
 5             Console.Write("{0,3}",i);
 6         }
 7         Console.WriteLine();
 8         IEnumerable<int> query1 = numbers.OrderBy(i => i);//按照升序排列
 9         foreach (var i in query1)
10         {
11             Console.Write("{0,3}", i);

12         }

 

 

同樣我們在numbers后點的時候會出現

就好像是Array內置的的方法一樣。

現在我們來看他的函數定義的特點:

public static IOrderedEnumerable<TSource> OrderByDescending<TSource, TKey>(
    this IEnumerable<TSource> source,
    Func<TSource, TKey> keySelector,
    IComparer<TKey> comparer
)

由此我們就可以斷定Linq內的函數都其實是擴展方法。

所有 LINQ 查詢操作都由以下三個不同的操作組成:
  1. 獲取數據源。 
  2. 創建查詢。 
執行查詢。
下面先看看MSDN的官方實例:
class IntroToLINQ
{        
    static void Main()
    {
        // The Three Parts of a LINQ Query:
        //  1. Data source.
        int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };

        // 2. Query creation.
        // numQuery is an IEnumerable<int>
        var numQuery =
            from num in numbers
            where (num % 2) == 0
            select num;

        // 3. Query execution.
        foreach (int num in numQuery)
        {
            Console.Write("{0,1} ", num);
        }
    }
}

 當然沒學LinQ我們依然可以老掉牙的這樣寫:

        int[] numbers = new int[7] { 0, 1, 2, 3, 4, 5, 6 };
        List<int> list = new List<int>();
        foreach (int num in numbers)
        {
            if (num % 2 == 0)
            {
                list.Add(num);
            }
        }
        foreach (int i in list)
        {
             Console.Write("{0,1} ", i); 
}

 現在我們這樣,利用lambda表達式簡化上面的官方示例,只需改一句即可完成相同的功能:

 int[] numbers = new int[7] { 60, 17, 12, 63, 35, 5, 6 };
        IEnumerable<int> query = numbers.Where(i=>i%2==0);

同時,官方示例內部實行時其實編譯器依舊將其轉化為lambda表達式的形式。

linq 的查詢語句看起來非常像SQL語句,但是和SQL無關。

現在來簡要的介紹一下常用的語句,和同學們一起學習下,我也是剛剛學的,不過了解這些還是皮毛,要深入的要就就要熟練掌握必須花時間去深入研究。

1.查詢表達式必須以 from子句開頭

  1. from 子句中引用的數據源的類型必須為 IEnumerable、 IEnumerable < T >或一種派生類型(如 IQueryable <T >)。
  2. 查詢表達式還可以包含子查詢,子查詢也是以 from 子句開頭。
2.where 子句用在查詢表達式中,用於指定將在查詢表達式中返回數據源中的哪些元素。
3. select 子句可以指定將在執行查詢時產生的值的類型。該子句的結果將基於前面所有子句的計算結果以及 select 子句本身中的所有表達式。查詢表達式必須以 select 子句或 group 子句結束。
4.group 子句返回一個 IGrouping <TKey, TElement >對象序列,這些對象包含零個或更多個與該組的鍵值匹配的項。
5.by 上下文關鍵字用在查詢表達式的 group 子句中,用於指定應該如何對返回的項進行分組。
6.在查詢表達式中,存儲子表達式的結果有時很有用,這樣可以在隨后的子句中使用。可以使用 let 關鍵字完成這一工作,該關鍵字可以創建一個新的范圍變量,並且用您提供的表達式的結果初始化該變量。一旦用值初始化了該范圍變量,它就不能用於存儲其他值。但如果該范圍變量存儲的是可查詢的類型,則可以對其進行查詢。
7.在查詢表達式中, orderby 子句可使返回的序列或子序列(組)按升序或降序排序
8.使用 join 子句可以將來自不同源序列並且在對象模型中沒有直接關系的元素相關聯。
9.ascending 上下文關鍵字用在查詢表達式的 orderby 子句中,用於指定從最小到最大的排序順序。
10.descending 上下文關鍵字用在查詢表達式的 orderby 子句中,用於指定從最大到最小的排序順序。
11.equals 上下文關鍵字用在查詢表達式的 join 子句中,用於比較兩個序列的元素。
12.可以使用 into 上下文關鍵字創建一個臨時標識符,以便將 group、 join 或 select 子句的結果存儲到新的標識符中。
到此我們先熟悉一下這幾個查詢關鍵字,做個小測試:
 static void Main()
    {
        string values = "abcdefasfsafcdekjekyursdjdagghiaaefrxfgs";
        var query =
                 from value in values
                 group value by value into repeatGroup//通過使用into標識符,可以對每個組調用 Count 方法
                 orderby repeatGroup.Count() descending//降序排列
                 select new { 字符 = repeatGroup.Key, 次數 = repeatGroup.Count() };
        //通過select返回匿名類型對象實現返回多列的效果
        foreach (var cha in query)
        {
            Console.WriteLine(cha.字符 + "=" + cha.次數);
        }
        Console.ReadKey();
    }

剛好發現一個嵌套的form子句的例子,里面使用let子句來保存臨時變量,也就是句子拆成單詞再返回合適條件的。

 

 1 using System;
 2 using System.Linq;
 3 class LetSample1
 4 {
 5     static void Main()
 6     {
 7         string[] strings = 
 8         {
 9             "A penny saved is a penny earned.",
10             "The early bird catches the worm.",
11             "The pen is mightier than the sword." 
12         };
13 
14         var earlyBirdQuery =
15             from sentence in strings
16             let words = sentence.Split(' ')
17             from word in words
18             let w = word.ToLower()  
19             where w[0] == 'a' || w[0] == 'e'
20                 || w[0] == 'i' || w[0] == 'o'
21                 || w[0] == 'u'
22             select word;
23 
24         // Execute the query.
25         foreach (var v in earlyBirdQuery)
26         {
27             Console.WriteLine("\"{0}\" starts with a vowel", v);
28         }
29 
30         // Keep the console window open in debug mode.
31         Console.WriteLine("Press any key to exit.");
32         Console.ReadKey();
33     }
34 }

 

 

 

下面的方法都是IEnumerable<T>的擴展方法:
Average計算平均值;

Min最小元素;

Max最大元素;

Sum元素總和;

Count元素數量;
Concat連接兩個序列;
Contains序列是否包含指定元素;
Distinct取得序列中的非重復元素;
Except獲得兩個序列的差集;
Intersect獲得兩個序列的交集;
First取得序列第一個元素;
Single取得序列的唯一一個元素,如果元素個數不是1個,則報錯

FirstOrDefault 取得序列第一個元素,如果沒有一個元素,則返回默認值;
Linq只能用於范型的序列,IEnumerable<T>,對於非范型,可以用Cast或者OfType
IEnumerable的方法:
Cast<TResult>:由於Linq要針對范型類型操作,對於老版本.Net類等非范型的IEnumerable序列可以用Cast方法轉換為范型的序列。ArrayList l; IEnumerable<int> il = l.Cast<int>();
OfType<TResult>:Cast會嘗試將序列中所有元素都轉換為TResult類型,如果待轉換的非范型序列中含有其他類型,則會報錯。OfType則是只將序列中挑出指定類型的元素轉換到范型序列中。
Linq對於小數據量、對性能要求不高的環節用linq很方便,而且延遲加載機制降低了內存占用,比一般人寫的程序效率都高。 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM