C#新特性:匿名類和擴充套件方法

銀河使者發表於2008-06-12
本文為原創,如需轉載,請註明作者和出處,謝謝!

一、用var定義變數

    在C#3.0中提供了一種新的宣告變數的方式,這就是var。通過這個關鍵字,在宣告變數時就無需指定型別了,變數型別是在初始化時由編譯器確定的。程式碼如下:

var ss = "abcd";
MessageBox.Show(ss.GetType().ToString());

上面的程式碼將顯示System.String,從而證明C#編譯器已經將ss編譯成了String變數了。而在輸出ss後,再輸入“.”後,會看到將String型別變數的相應方法和屬性也列出來了,因此可以斷定,C#將ss看成了String型別,而不是Object。所以使用var定義變數同時可以擁有Object和強型別的優點。
     不過大家不要將var看成是javascript的var,它們的區別是,javascript是弱型別的語言,而且javascript中的變數(也包括用var宣告的變數)可以變換型別,如下面的javascript所示:

var s = "abcd";
s=3;
alert(s);

    上面的程式碼第一次給s賦了一個字串,而第二行程式碼又給賦了一個整數。這樣的程式碼在javascript中沒有任何問題。但在C#3.0中,var變數一但被初始化,確定型別後,就無法改變型別了。如下面的程式碼是無法編譯通過的:

var ss = "abcd";
ss = 44;

    綜上所述,在使用var定義變數時有以下四個特點:

1.   必須在定義時初始化。也就是必須是var s = “abcd”形式,而不能是如下形式:
var s;
s = “abcd”;

2.   一但初始化完成,就不能再給變數賦與初始化值型別不同的值了。

3.  var要求是區域性變數。

4.  使用var定義變數和object不同,它在效率上和使用強型別方式定義變數完全一樣。但筆者建議如果事先知道變數的型別,儘量使用強型別方式來宣告變數。否則,就會造成由於大量使用var,而使得開發人員很難斷定某個變數是什麼型別。這樣不利於程式的維護和升級。

    雖然var有利有弊,但筆者個人認為,如果將動態語言轉換成C#語言,可以考慮使用var來定義變數。這是因為動態語言沒有型別,而要將其轉換成強型別的C#語言,就必須給變數指定個型別,但事先確定型別是很費勁的,不如將其指定成var,再由C#編譯器去確定變數的具體型別。那麼如果在轉換的過程中,發現動態語言的變數改變了型別,該怎麼辦呢?這個可以使用第三部分要講的“匿名類”來解決這個問題。

二、初始化
    如果一個類有public欄位,在建立類的物件例項時可以使用下面的程式碼來初始化這些欄位;

public class MyClass
{
    public String field1;
    public int field2;
    public bool field3;
}

MyClass my = new MyClass();
my.field1 = “abcd”;
my.field2 = 44;
my.field3 = true;

    在C#3.0中提供了一種更簡便的方法來初始化這些public變數,程式碼如下:

MyClass my = new MyClass
{
    field1 = “abcd”,
    field2 = 44;
    field3 =true;
};

    上面的程式碼的寫法有些象帶引數的構造方法,但這將不是呼叫了MyClass的構造方法(因為MyClass並沒有帶三個引數的構造方法),而只是C#編譯器玩的一個魔術。實際上,上面的程式碼在編譯後,仍然和使用傳統的初始化欄位的方法一樣。只是在語法上看起來更簡單(至少不用寫那麼多個my)。要注意的的,使用這種方法初始化,必須是public的欄位(不能是protected、private或預設修飾符的欄位)。
    在C#3.0中還改進了對集合類的初始化方式(使其初始化的方式類似於陣列)。但遺憾的是,這種初始化方式只支援用泛型的集合類,也就是說,只有實現了System.Collections.Generic.違規廣告ection的集合類才可以使用這種初始化方法。程式碼如下:

List myList = new List { "data1", "data2", "data3" };
foreach (string data in myList)
{
    textBox1.AppendText(data);
}

三、匿名類

    在C#3.0中提供了一種新的建立類的方法,程式碼如下:
var my = new
{
    field1 = "abcd",
    field2 = 12
};
MessageBox.Show(my.field1);

    C#編譯器會自動推斷my是一個有兩個public欄位的類的物件例項。也就是說相當於下面的程式碼:

public class MyClass
{
    public String field1;
    public int field2;
}

var my = new MyClass();
my.field1 = "abcd";
my.field2 = 25;
MessageBox.Show(my.field1);

    在第一部分講到如果動態語言在給變數賦值的過程中改變了變數型別,如果將其轉換為強型別語言。當然,一種方法是將變數宣告成object型別,或是使用匿名類來解決這個問題。程式碼如下:
var myVar = new
{
    field_string = “abcd”
    field_int = 12;
};

    然後根據當前這個變數所使用的型別來決定該使用哪個類欄位。

四、擴充套件方法

    這個世界上總是存在著很多奇妙的東西。然而,在這部分所介紹的擴充套件方法就是其中之一。從字面上看可能讀者很難猜透“擴充套件方法”是什麼意思。然而,看了下面的例子,就會感覺到非常的奇妙。

namespace ExtMethod
{
    public class Class1
    {
        public String s = "bill";
    }
    public class Class2 : Class1
    {
    }
    public static class AnyClassName
    {
        public static String getName(this Class1 class1)
        {
            return class1.s + class1.s;  
        }
    }

    public partial class Form1 : Form
    {
         
        private void button1_Click(object sender, EventArgs e)
        {
            Class1 c = new Class1();
            MessageBox.Show(c.getName());            
            Class2 c = new Class2();
            MessageBox.Show(c.getName());            
        }
    }
}

    看到上面的程式碼,也許很多人會感到奇怪,在Class1和Class2中並沒有getName方法,怎麼在呼叫時出來個getName方法呢?實際上,這就是擴充套件方法的用法,從本質上說,擴充套件方法就是將靜態方法(必須宣告成static)插入到某個類和其子類中(也就是說,在這些類中可以使用在外部定義的靜態方法)。那麼要往哪個類中插入呢?這就要在定義靜態方法時指定了。大家可以看看getName方法的第一個引數,使用了this關鍵字,這就表明這個方法是一個擴充套件方法,後面的型別就是要插入該方法的類,在本例中是Class1,也就是說在Class1及其子類中都可以使用getName方法。上面的呼叫程式碼也相當於下面的程式碼:
Class2 c = new Class2();
MessageBox.Show(AnyClassName.getName(c));

    但使用c.getName可能會更好一些,而且也降低了對靜態方法所在的類(AnyClassName)的依賴性。

    在使用擴充套件方法時應注意以下幾點:

1.  擴充套件方法所在的類名可以是任意合法的類名。

2.  擴充套件方法所在的類必須和使用擴充套件方法的程式碼在同一個名稱空間裡,否則無法編譯通過。

3.  在本例中,Class1和Class2只能宣告成public,因為AnyClassName被宣告為public。如果AnyClassName不加修飾符,Class1和Class2也可以不加修飾符,當然,也可以被宣告為public。也就是說,Class1和Class2必須有比AnyClassName具有更強的訪問性。如下面程式碼所示:
    class Class1
    {
        public String s = "bill";
    }
    class Class2 : Class1
    {
    }
    static class AnyClassName  // 這時如果前面加public是無法編譯通過的。
    {
        public static String getName(this Class1 class1)
        {
            return class1.s + class1.s;  
        }
    }

4.   如果在Class1或Class2中已經有getName方法了,那麼Class1或Class2中的getName優先順序更高。也就是說,擴充套件方法是無法覆蓋原類中的同名(引數名和型別也相同)的方法的。

    擴充套件方法尤其在很多類需要同樣的方法,而這些類又無法繼承其它類時特別有用。當然,在要對某個類進行擴充套件,但我們並沒有原始碼時,擴充套件方法也可以派上用場。

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/12921506/viewspace-344511/,如需轉載,請註明出處,否則將追究法律責任。

相關文章