本文例子大部分來自於(Apress pro linq)
為了使LINQ可以無縫的和C#語言整合在一起,微軟對C#3.0加入了些新功能,這裡主要介紹和LINQ相關的加強。
1、var關鍵字,集合初始化以及匿名型別
2、Lambda 運算式
3、部分(Partial )方法
4、擴充方法
5、運算式樹狀架構
1、var關鍵字,集合初始化以及匿名型別
var:
可以賦予局部變數推斷“類型”var 而不是顯式類型。var 關鍵字指示編譯器根據初始化語句右側的運算式推斷變數的類型。推斷類型可以是內建類型、匿名型別、使用者定義型別、.NET Framework 類庫中定義的類型或任何錶達式。var只是表示由編譯器確定和分配最適當的類型。和javascript中的var的概念不同.例如:var i = 5;編譯完之後i就是一個整型,和int i=5;沒有任何區別.在很多情況下,var 是可選的,它只是提供了文法上的便利。
請看如下例子:
var i = 5;//int
var j = 23.56;//double
var k = "C Sharp";//string
var x;//錯誤
var y = null;//錯誤
var z = { 1, 2, 3 };//錯誤
對象和集合初始設定式:
Code
class user
{
public int ID { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
User user = new User();
user.Id = 1;
user.Name = “lfm";
user.Age = 22;
在VS2008中,編譯器會自動地產生合適的屬性setter代碼,使得原來幾行的屬性賦值操作可以在一行完成。我們可以這樣簡化:像這樣,對象初始化器由一系列成員對象組成,其對象必須初始化,用逗號間隔,使用{}封閉。
User user = new User { Id = 1, Name = “lfm", Age = 22 };
又例如,我把二個人加到一個基於泛型的類型為User的List集合中:
List<User> user = new List<User>{
new User{Id=1,Name=“lfm",Age=22},
new User{Id=2,Name=“tmx",Age=25},
};
匿名型別:
匿名型別提供了一種方便的方法,可用來將一組唯讀屬性封裝到單個對象中,而無需首先顯式定義一個類型。類型名由編譯器產生,並且不能在原始碼級使用。這些屬性的類型由編譯器推斷。
匿名型別允許定義行內類型,無須顯式定義類型。在將匿名型別分配給變數時,必須使用 var 構造初始化該變數。
//屬性也不需要申明,這些屬性的類型由編譯器推斷
var p1 = new { Id = 1, Name = “lfm", Age = 22 };
var p2 = new { Id = 2, Name = “tmx", Age = 25 };
p1 = p2;//p1,p2結構相同,可以互相賦值
在這裡編譯器會認為p1,p2相當於:
public class SomeType
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
備忘(from msdn)
匿名型別是直接從對象派生的參考型別。儘管應用程式無法訪問匿名型別,但編譯器仍會為其提供一個名稱。從公用語言運行庫的角度來看,匿名型別與任何其他參考型別沒有什麼不同。
如果兩個或更多個匿名型別以相同的順序具有相同數量和種類的屬性,則編譯器會將這些匿名型別視為相同的類型,並且它們共用編譯器產生的相同類型資訊。
匿名型別具有方法範圍。若要向方法邊界外部傳遞一個匿名型別或一個包含匿名型別的集合,必須首先將匿名型別強制轉換為對象。但是,這會使匿名型別的強型別化無效。如果必須儲存查詢結果或者必須將查詢結果傳遞到方法邊界外部,請考慮使用普通的命名結構或類而不是匿名型別。
2、Lambda 運算式
在c#3.0中微軟加入了“Lambda 運算式”,“Lambda 運算式”的概念最早是數學家Alonzo Church在1936年提出的,早在LISP語言中就已經得到應用。這些運算式提供了簡短的文法來表達一個運演算法則。在C#3.0中“Lambda 運算式”是一個匿名函數,它可以包含運算式和語句,並且可用於建立委託或運算式分類樹類型。在我們詳細講解“Lambda 運算式”之前,我們先來瞭解一下“Lambda 運算式”出現的原因。
我們假設這樣一個情境,我們希望有這樣一個函數,對一個整型數組進行過濾,而過濾得條件在編寫函數時還不知道,直到使用這個函數的時候可以根據當前的情況編寫過濾條件函數,大家一看到這個情境可能馬上想到,可以使用委託阿。ok,代碼如下:
public class Common
{
public delegate bool IntFilter(int i);
public static int[] FilterArrayOfInts(int[] ints, IntFilter filter)
{
ArrayList aList = new ArrayList();
foreach (int i in ints)
{
if (filter(i))
{
aList.Add(i);
}
}
return ((int[])aList.ToArray(typeof(int)));
}
}
public class Application
{
public static bool IsOdd(int i)
{
return ((i & 1) == 1);
}
}
static void Main(string[] args)
{
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] oddNums = Common.FilterArrayOfInts(nums, Application.IsOdd);
foreach (int i in oddNums)
Console.WriteLine(i);
}
結果為:
1
3
5
7
9
看了這段代碼之後,大家可能會覺得這個實現可能不夠好,這裡的IsOdd函數可能只用這麼一次,並且也太簡單了,不值得當成一個函數來用,於是我們想到了C#2.0的匿名委託,實現代碼如下:
static void Main(string[] args)
{
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] oddNums =
Common.FilterArrayOfInts(nums, delegate(int i) { return ((i & 1) == 1); });
foreach (int i in oddNums)
Console.WriteLine(i);
}
恩,這段代碼似乎相當棒了,解決了我剛才說的所有問題,但是看起來好象不夠簡潔,也比較難懂些。我希望有一個更好的方式來解決這個問題。
我們先來看看“Lambda 運算式”的用法,所有 Lambda 運算式都使用 Lambda 運算子 =>,該運算子讀為“goes to”。該 Lambda 運算子的左邊是輸入參數(如果有),右邊包含運算式或語句塊。Lambda 運算式 x => x * x 讀作“x goes to x 乘x”其意義就是輸入x返回x乘x。
Lambda 運算式返回運算式的結果,並採用以下基本形式:
(input parameters) => {statement;}
多個參數時可以使用如下方式
(param1, param2, …paramN) => expr
更為複雜的方式可以如下表示
(param1, param2, …paramN) =>
{
statement1;
statement2;
…
statementN;
return(lambda_expression_return_type);
}
這裡大家需要知道的是“Lambda 運算式”其實就是一個匿名委託,可以使用“Lambda 運算式”的地方必須是可以使用委託的地方,Lambda 運算子 =>左邊的是輸入參數,Lambda 運算子 =>右邊的部分是執行後的輸出結果
比如
x => x.Length > 0
相當於
delegate(string x) { return x.Length > 0; }
大家理解了“Lambda 運算式”,我們就可以使用“Lambda 運算式”來完成剛才過濾資料的情境了。代碼如下:
static void Main(string[] args)
{
int[] nums = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int[] oddNums = Common.FilterArrayOfInts(nums, i => ((i & 1) == 1));
foreach (int i in oddNums)
Console.WriteLine(i);
}
這段代碼比剛才匿名委託的方式易讀性要好很多了吧