這兩天看了一下msdnwebcast上的visual studio 2008的系列課程,記錄下所學的知識,以便加深記憶
1.匿名型別
顧名思義 匿名型別就是沒有名字的類型。在C#3.0中允許我們在程式中聲明一個臨時的類型來儲存資料,例如:
| 代碼如下 |
複製代碼 |
class Program { static void Main(string[] args) { //聲明一個匿名對象,擁有 Name和Age 屬性 var obj = new { Name = "Joey", Age = 25 };//這裡的new { Name = "Joey", Age = 25 } 就是一個匿名型別 ,obj則是這個類型的一個對象,稱為匿名對象 Console.WriteLine("匿名對象obj : Name=" + obj.Name + " , Age=" + obj.Age); } } |
上述代碼中,我們聲明了一匿名對象obj ,然後輸出對象的屬性值。如果在VS 你將滑鼠移到 obj前面的var 上面,vs 會提示:obj 是一個匿名型別 ‘a 。這個‘a 是編譯器自動作為標識的一個類型,匿名對象在編譯時間,編譯器還是得給它一個類型。其實上面的匿名型別 new { Name = "Joey", Age = 25 } 是直接從Object繼承過來的, 相當於
| 代碼如下 |
複製代碼 |
public class 'a{ public string Name{get;private set;} public int Age{get;private set;} } |
這樣的一個擁有唯讀屬性的自訂類型。
在MSDN 中匿名型別的定義是這樣的:
1.匿名型別提供了一種方便的方法,可用來將一組唯讀屬性封裝到單個對象中,而無需首先顯式定義一個類型。
2.類型名由編譯器產生,並且不能在原始碼級使用。每個屬性的類型由編譯器推斷。
3.可通過使用 new 運算子和對象初始值建立匿名型別。
上面三句話是所謂的”說到點子上了”.讓人一看就明白匿名型別。但是在這裡,我還得提一提,匿名型別和var 隱式類型化的聲明關鍵字的關係;很多新手在看到var 聲明時,就覺得這是一個匿名對象,匿名型別的對象是必須用var 來聲明,但是用var 聲明的對象不一定都是匿名對象,例如 var n=5; 你就不能說n 是一個匿名對象,n只是一個隱式類型化的局部變數;而 var s=new{ S1="abc",S2="def"}; s 則是一個 類型為匿名型別的對象。也就是說 匿名對象是 用var 聲明的一個 在記憶體中臨時的類型的對象。它的類型不能像隱式類型那樣根據右邊的執行個體畫運算式來推斷類型,它是一個實實在在的匿名型別,而var i=5; 這種隱式類型的聲明,編譯時間,i其實還是 int32 類型,隱式類型只是一種文法糖。
那匿名型別一般都在什麼情況下使用呢?
1.匿名型別通常用在查詢運算式的 select 子句中,以便返回源序列中每個對象的屬性子集(Linq 中使用的比較多)
匿名型別包含一個或多個公用唯讀屬性。 包含其他種類的類成員(如方法或事件)為無效。 用來初始化屬性的運算式不能為 null、匿名函數或指標類型。
最常見的方案是用其他類型的屬性初始化匿名型別。在下面的樣本中,假定名為 Product 的類存在。 類 Product 包括 Color 和 Price 屬性,以及您不感興趣的其他屬性。 變數 products 是 Product 對象的集合。 匿名型別聲明以 new 關鍵字開始。 聲明初始化了一個只使用 Product 的兩個屬性的新類型。 這將導致在查詢中返回較少數量的資料。
如果您沒有在匿名型別中指定成員名稱,編譯器會為匿名型別成員指定與用於初始化這些成員的屬性相同的名稱。 必須為使用運算式初始化的屬性提供名稱,如下面的樣本所示。 在下 面樣本中,匿名型別的屬性名稱都為 Color 和 Price。
var productQuery = from prod in products
select new { prod.Color, prod.Price };
foreach (var v in productQuery)
{
Console.WriteLine("Color={0}, Price={1}", v.Color, v.Price);
}
上面的查詢中使用一個匿名對象儲存了查詢的結果。 new { prod.Color,prod.Price} 匿名型別,會使用 查詢結果prod 對象的Color屬性和Price屬性作為新的匿名對象的屬性。
可通過將隱式鍵入的本地變數與隱式鍵入的數組相結合建立匿名鍵入的元素的數組,如下面的樣本所示。
var anonArray = new[] { new { name = "apple", diam = 4 }, new { name = "grape", diam = 1 }};
上面的代碼是將兩個匿名對象存入一個匿名的數組中。需要注意的是,在上面代碼中,匿名數組儲存的匿名對象的初始化器裡的屬性類型和屬性名稱和屬性的數量必須一致。也就是說
new { name = "apple", diam = 4 }=new { name = "grape", diam = 1 } 成立的條件是: 屬性名稱字和屬性儲存區的值的類型以及屬性個數必須全部相同,vs 編譯器才會認為這兩個匿名型別是同一個類型。這樣的兩個匿名型別才能夠存入 anonArray 這樣的一個匿名數組中。因為匿名數組也只能儲存同一種類型的匿名對象。
2.擴充方法
1.擴充方法(Extension method),可以對現有類功能進行擴充,從而使該類型的執行個體具有更多的方法(功能)。比如你我從第三方的廠商那裡擷取到了一個dll程式集,而我們要對該程式集的類擴充某寫功能,我們就可以使用擴充方法來對該類進行擴充。
2.Extension Method僅僅是看起來像是一個類型的方法,但其實質上不是,它更像是靜態類型的靜態方法,事實上,它確實擁有靜態方法所具有的所有功能 。這點在下面實現擴充方法的時候就會明白,其實擴充方法就是一靜態方法。
3.Extension Method的範圍是整個namespace可見的,並且可以通過using namespace來匯入其它命名空間中的Extension Method
4.編譯器產生的中繼語言(IL) 會將代碼轉換為對靜態方法的調用。因此,並未真正違反封裝原則。
5.實際上,擴充方法無法訪問它們所擴充的類型中的私人變數。
約定:
1.可以使用擴充方法來擴充類或介面,但不能重寫擴充方法。
2.與介面或類方法具有相同名稱和簽名的擴充方法永遠不會被調用。
3.編譯時間,擴充方法的優先順序總是比類型本身中定義的執行個體方法低。
換句話說,如果某個類型具有一個名為Process(int i) 的方法,而您有一個具有相同簽名的擴充方法,則編譯器總是綁定該執行個體方法。
4.當編譯器遇到方法調用時,它首先在該類型的執行個體方法中尋找匹配的方法。
如果未找到任何匹配方法,編譯器將搜尋為該類型定義的任何擴充方法,並且綁定到它找到的第一個擴充方法
MSDN中是這麼定義的:
擴充方法使您能夠向現有類型“添加”方法,而無需建立新的衍生類別型、重新編譯或以其他方式修改原始類型。擴充方法是一種特殊的靜態方法,但可以像擴充類型上的執行個體方法一樣進行調用。
| 代碼如下 |
複製代碼 |
{ class Program { static void Main(string[] args) { var stu = new Student() { Name = "joey", Age = 25 }; //調用執行個體方法 Console.WriteLine(stu.ToString()); //調用擴充方法 。調用的時候會vs 智能感知會在方法邊上加個向下的箭頭,表示這是一個擴充方法 Console.WriteLine(stu.Hello()); } } public class Student { public string Name { get; set; } public int Age { get; set; } //重寫ToString方法 public new string ToString() { return "Name: " + this.Name + "nAge: " + this.Age; } } public static class ExtendMehods { //使用this 關鍵字擴充類的方法 public static string Hello(this Student stu) { return "嗨!大家好!我叫 " + stu.Name + ",我今年 " + stu.Age + " 歲"; } } } |
其實擴充方法就是一個靜態非泛型類中的一個靜態方法。我們也可以使用靜態類名點方法名的方式調用。