电竞比分网-中国电竞赛事及体育赛事平台

分享

C# 3.0 特性預(yù)覽

 昵稱10504424 2013-02-28
聲明:本文主要是根據(jù)微軟《C# 3.0 Specification》文檔翻譯而成(本文內(nèi)容并非完整按照該文檔進(jìn)行編排),并對(duì)msdn相關(guān)文檔中的資料進(jìn)行整理而成。若有翻譯不妥之處,懇請(qǐng)指正。
    
    閱讀本文前,需要了解:
    1、C# 3.0代號(hào)“Orcas”,是基于C# 2.0的擴(kuò)展。提供了多種具有更高層次功能的類庫(kù)。這些擴(kuò)展允許構(gòu)造組合(compositioanl)API來(lái)實(shí)現(xiàn)具有同關(guān)系型數(shù)據(jù)庫(kù)及XML等領(lǐng)域相等效能的表達(dá)效力。
    2、LINQ項(xiàng)目可以看作是一個(gè)未來(lái)技術(shù)的演示項(xiàng)目,可以從MSDN網(wǎng)站上下載預(yù)覽包。LINQ項(xiàng)目旨在擴(kuò)展C#及VB.NET在語(yǔ)法上對(duì)語(yǔ)言集成查詢的支持。借助這些特性,我們可以用類似SQL或者XQuery之類的語(yǔ)句進(jìn)行代碼編寫。LINQ項(xiàng)目的內(nèi)容不單獨(dú)介紹,因?yàn)樗鼘?duì)于C#中的特性主要就是C# 3.0中的語(yǔ)言集成查詢特性。
    3、寫這篇文章的目的很簡(jiǎn)單,就是希望有興趣的朋友可以開始3.0的探索了,這樣當(dāng)3.0的編譯器出臺(tái)時(shí)不至于再趕時(shí)間學(xué)習(xí)。并不建議初學(xué)者花費(fèi)精力來(lái)掌握本文內(nèi)容,了解一下發(fā)展概況即可,否則很容易導(dǎo)致兩頭都搞不好。況且這并不是最基礎(chǔ)的內(nèi)容。
    4、本文內(nèi)容僅基于預(yù)覽版本內(nèi)容(PDC 2005 Technology Preview),并非最終版本。C# 3.0完成后,有可能會(huì)增加或者更改某些特性。預(yù)覽版本可能還不能支持C# 3.0中的某些內(nèi)容,對(duì)于這些內(nèi)容,將簡(jiǎn)單介紹。
    5、本文旨在將新的特性展現(xiàn)出來(lái),針對(duì)每個(gè)特性并不進(jìn)行深入的探討,讀者如果有興趣可以自行參閱相關(guān)資料。
    
    那么,我們就開始吧。
    
    C# 3.0的擴(kuò)展特性主要包括以下幾點(diǎn),我們?cè)诤竺嬉矔?huì)按照這個(gè)順序進(jìn)行介紹:
    1、隱式局部變量(implicitly typed local variables),通過(guò)初始化該局部變量的表達(dá)式自動(dòng)推斷出該變量的類型。
    2、擴(kuò)展方法(extention methods),可以利用附加方法拓展已經(jīng)存在的類型和構(gòu)造類型。
    3、Lambda表達(dá)式(lambda expressions),匿名方法的革新,能夠提供更好的類型推導(dǎo)以及到委托類型和表達(dá)式樹的轉(zhuǎn)換。
    4、表達(dá)式樹(expression trees),允許Lambda表達(dá)式以數(shù)據(jù)(表達(dá)式樹)的形式存在,而不是代碼(委托)。
    5、對(duì)象初始化器(object initializer),簡(jiǎn)化了對(duì)象的構(gòu)造和初始化。
    6、匿名類型(anonymouse types),由對(duì)象初始化器自動(dòng)推斷和生成的元組類型。
    7、隱式數(shù)組(implicitly typed array),一種數(shù)組創(chuàng)建和初始化的形式,可以從數(shù)組初始化器推導(dǎo)出數(shù)組的元素類型。
    8、查詢表達(dá)式(query expressions),提供語(yǔ)言集成查詢的語(yǔ)法,使得在編程中可以使用類似關(guān)系型(如SQL)以及層次(如XQuery)查詢語(yǔ)言的代碼。   
          
    
一、隱式局部變量
    在以前,如果我們要聲明局部變量,比如:
    int i = 5;
    string str = "test";
    int[] numberss = new int[]{1, 2, 3);
    ArrayList list = new ArrayList();
    在C# 3.0,完全可以等價(jià)為
    var i = 5;
    var str = "test";
    var numberss = new int[]{1, 2, 3);
    var list = new ArrayList();
    我們可以看到,用一個(gè)簡(jiǎn)單的var就可以代替這么些類型(懷念Pascal中~~~)。因?yàn)椋覀兺灰P(guān)注后面的賦值表達(dá)式即可推斷出變量的類型,在這里也是如此。
    
    不過(guò)呢,自然要有運(yùn)用條件:
    1、出于向后兼容的考慮,如果作用域中出現(xiàn)一個(gè)叫做var的類型,那么會(huì)優(yōu)先使用var類型來(lái)聲明該變量而不再推斷表達(dá)式的類型。
    2、聲明隱式變量的同時(shí)需要初始化。而初始化需要一個(gè)表達(dá)式,且該表達(dá)式不能是單獨(dú)的對(duì)象或者集合初始化器,但可以是包含對(duì)象或者集合初始化器的new表達(dá)式。如var x;和var x = {1 ,2 ,3};都是錯(cuò)誤的。
    3、表達(dá)式的編譯時(shí)類型不能為空類型(null)。如var x = null;就是錯(cuò)誤的。
    4、如果隱式變量的聲明中包含多個(gè)聲明符,那么這些聲明符必須具備同樣的編譯時(shí)類型。
    
    此外,for語(yǔ)句中的初始化部分以及using語(yǔ)句的資源聲明中,都可以使用隱式變量。同樣的,使用foreach迭代也可以利用隱式變量:
    int[] numbers = {1, 2, 3, 4};
    foreach(var n in numbers) Console.WriteLine(n);
   
    
二、擴(kuò)展方法
    通過(guò)在方法的第一個(gè)參數(shù)中用this關(guān)鍵字修飾,即可聲明一個(gè)擴(kuò)展方法。但是,擴(kuò)展方法必須聲明于靜態(tài)類中。
    namespace TestSpace{
        public static class Extensions{
            public static int ToInt32(this string s){
                return int.Parse(s);
            }
        }
    }
    這些擴(kuò)展方法享有普通靜態(tài)方法等同的功能。此外,只要引入了擴(kuò)展方法,我們就可以通過(guò)普通的實(shí)例方法調(diào)用語(yǔ)法來(lái)調(diào)用它們。
    
    那么,我們現(xiàn)在就介紹如何引入擴(kuò)展方法。
    相信大家對(duì)于using很熟悉了吧。using System;相當(dāng)于引入了System命名空間中的所有類型。不過(guò)呢,using在這里的意義可不單單是引入了類型,還引入了屬于該命名空間下的擴(kuò)展方法。擴(kuò)展方法被引入之后,擴(kuò)展方法中的第一個(gè)參數(shù)(即被this修飾的參數(shù))的類型便多了這么一個(gè)附加方法。并且該附加方法的優(yōu)先級(jí)比該類型的同名實(shí)例方法要低。比如要使用上例中的擴(kuò)展方法,先引入:using TestSpace;然后可以這樣調(diào)用:
    string str = "1234";
    int re = str.ToInt32();
    //上式等價(jià)于int re = Extentions.ToInt32(s);
    
    經(jīng)過(guò)這兩個(gè)例子,我們可以看出:
    1、擴(kuò)展方法必須聲明在靜態(tài)類中(這樣也能保證自身是靜態(tài)方法)。
    2、擴(kuò)展方法聲明時(shí)至少有一個(gè)參數(shù),即要附加的實(shí)例類型,并且要用this來(lái)修飾。
    3、擴(kuò)展方法在作為附加方法調(diào)用時(shí)必然成為實(shí)例方法。
    4、擴(kuò)展方法在作為附加方法調(diào)用時(shí)的優(yōu)先級(jí)比同名的實(shí)例方法要低。即編譯器默認(rèn)調(diào)用實(shí)例類型的原有方法,如果找不到澤調(diào)用擴(kuò)展方法。    
       
       
三、Lambda表達(dá)式
    通過(guò)C# 2.0帶來(lái)的匿名方法,我們已經(jīng)發(fā)現(xiàn)了簡(jiǎn)化代碼的一種方法。但是,這樣的做法仍然有些冗長(zhǎng)且具有強(qiáng)制性,所以C# 3.0中又引入了Lambda表達(dá)式。
    
    Lambda表達(dá)式寫成參數(shù)列表形式,后面緊跟“=>”符號(hào),再跟上一個(gè)表達(dá)式或者代碼塊。只要看一下后面給出的例子,Lambda表達(dá)式的寫法就很容易掌握,所以這里不再給出完整的Lambda表達(dá)式定義式。
    并且,Lambda表達(dá)式中的參數(shù)可以是顯式,也可以是隱式的。顯式參數(shù)列表中參數(shù)類型是顯式指定的,而隱式參數(shù)列表中的參數(shù)類型會(huì)根據(jù)上下文而定(特別地,當(dāng)Lambda表達(dá)式轉(zhuǎn)換為兼容委托時(shí),由這個(gè)委托提供參數(shù)的具體類型)。
    此外,如果參數(shù)列表中只有一個(gè)隱式的參數(shù),那么圓括號(hào)可以省略,如:
    ( param ) => expression
    可簡(jiǎn)化為
    param => expression
    
    那么,我們來(lái)看一下例子:
    (int x) => x + 1    //顯式參數(shù)列表
    x => x + 1        //隱式參數(shù)列表,由于只有一個(gè)隱式的參數(shù),所以沒(méi)有圓括號(hào)
    () => Console.WriteLine("Hello")        //無(wú)參數(shù)
    (int x, int y) => x + y    //兩個(gè)顯式參數(shù)
    x => { return x + 1; }    /*帶有代碼塊的隱式Lambda表達(dá)式。但該功能目前尚不被PDC 2005 Technology Preview所支持,所以下文中將不再討論該功能的運(yùn)用。*/
    相信大家看完這些例子以后就知道怎么使用Lambda表達(dá)式了吧(注意:這里說(shuō)的只是表達(dá)式,僅是語(yǔ)句的一部分,相信細(xì)心的同志們已經(jīng)發(fā)現(xiàn)例子中沒(méi)有結(jié)尾分號(hào)了吧)。
    然后我們看一下將Lambda表達(dá)式融入語(yǔ)句中的示例,同時(shí)給出用匿名方法實(shí)現(xiàn)的等效做法來(lái)作對(duì)比(其中利用Func泛型委托):
    //sample 1
    Func<int, int> add = x => x + 1;    //隱式表達(dá)式
    //等價(jià)于
    Func<int, int> add =
        delegate(int x){
            return x + 1;
        };
    
    //sample 2
    Func<string, bool> filter = s => s == "123";    //隱式表達(dá)式
    //等價(jià)于
    Func<string, bool> filter =
        delegate(string s){
            return s == "123";
        };
    由此可以看出,Lambda表達(dá)式簡(jiǎn)化了的確不少。更重要的是,它可以將一段匿名方法簡(jiǎn)化為一個(gè)表達(dá)式,這樣就為了嵌套使用提供了保障,我在后面的其它部分中也會(huì)涉及到嵌套調(diào)用。
    
    
四、表達(dá)式樹
    表達(dá)式樹并不是PDC 2005標(biāo)準(zhǔn)的一部分,這里將簡(jiǎn)單闡述。
    表達(dá)式樹最根本的運(yùn)用就是System.Query.Expression<D>類型(有的材料上是System.Expressions.Expression<T>)。它為L(zhǎng)ambda表達(dá)式提供了一個(gè)內(nèi)存中高效的數(shù)據(jù)表示形式。
    對(duì)于下面這個(gè)Lambda表達(dá)式:
    Func<int, int> f = x => x + 1;
    這是代碼(委托)形式。而
    Expression<Func<int, int>> e = x => x + 1;
    就是一個(gè)對(duì)表達(dá)式樹的引用。
    
    Lambda表達(dá)式可以直接執(zhí)行,如int re = f(1);
    但是表達(dá)式樹的引用不可以,即int re = e(1);是錯(cuò)誤的。
    
    那么如何運(yùn)用表達(dá)式樹呢?
    表達(dá)式樹可以看作是對(duì)表達(dá)式委托的結(jié)構(gòu)體現(xiàn)??纯聪旅孢@個(gè)例子(轉(zhuǎn)),估計(jì)就比較有些理解了:
    Expression<Func<int, boo>> filter = n => n < 5;
    BinaryExpression body = (BinaryExpression)filter.Body;
    ParameterExpression left = (ParameterExpression)body.Left;
    ConstantExpression right = (ConstantExpression)body.Right;
    Console.WriteLine("{0} {1} {2}", left.Name, body.NodeType, right.Value);
    表達(dá)式樹的這種數(shù)據(jù)形式在后面將要談到的數(shù)據(jù)查詢方面是一個(gè)很好的輔助工具。   
       
       
五、對(duì)象和集合初始化器
    使用對(duì)象初始化表達(dá)式可以方便地初始化一個(gè)或者多個(gè)對(duì)象的屬性或者域。
    初始化表達(dá)式由“{”和“}”進(jìn)行封閉,同時(shí)內(nèi)部的成員初始化器用“,”進(jìn)行分隔。成員初始化器包括成員名稱、“=”和初始化的值。
    比如,對(duì)于類Dog:
    public class Dog{
        private string name;
        private int age;
        public string Name{
            get{
                return name;
            }
            set{
                name=value;
            }
        }
        public int Age{
            get{
                return age;
            }
            set{
                age=value;
            }
        }
    }
    這樣初始化:
    Dog dog = new Dog{
        Name = "Toddy", Age = 5
    };
    等價(jià)于
    Dog dog = new Dog();
    dog.Name = "Toddy";
    dog.Age = 5;
    當(dāng)然,嵌套的初始化也是允許的。
    
    同樣,集合初始化器使得集合可以像數(shù)組那樣初始化:
    List<int> list = new List<int>{1, 2, 3, 4};
    就這么簡(jiǎn)單……
    
    
六、匿名類型
    基于我們往往注重一個(gè)類型的結(jié)構(gòu)而不是名稱這個(gè)事實(shí),C# 3.0引入了匿名類型。
    隱式局部變量由編譯器自動(dòng)推測(cè)變量的類型,而匿名類型是讓編譯器自動(dòng)推測(cè)初始化表達(dá)式的類型。
    如第5節(jié)中的Dog類,我們其實(shí)也可以利用匿名類型來(lái)進(jìn)行初始化:
    var dog = new {Name = "Toddy", Age = 5};
    我們可以看到,上例中省略了類型名。
    有了匿名類型,我們無(wú)需了解這個(gè)類型叫做什么名字,只要符合這個(gè)結(jié)構(gòu)即可。
    
    
七、隱式數(shù)組
    從前面的種種特性中可以看到,利用編譯器根據(jù)上下文推導(dǎo)來(lái)識(shí)別是C# 3.0的很多更新的基礎(chǔ),本節(jié)的隱式數(shù)組也是如此。
    隱式數(shù)組會(huì)自動(dòng)根據(jù)數(shù)組內(nèi)容來(lái)識(shí)別數(shù)組類型。
    我們以前這樣寫:
    int[] arr = new int[]{1,2,3};
    現(xiàn)在可以這樣:
    var arr = new[] {1,2,3};        //一定要有[]操作符,參見隱式局部變量的注意部分
    
    但是必須要注意:
    1、數(shù)組中的內(nèi)容均可以隱性轉(zhuǎn)換為某一類型。
    2、最終類型不可為null。       
             
 
八、查詢表達(dá)式
    個(gè)人認(rèn)為,C# 3.0這些新特性的引入,不單單是為了在某種程度上簡(jiǎn)化平時(shí)的代碼開發(fā)量。更重要的是,它們?yōu)椴樵儽磉_(dá)式的方方面面都提供了良好的基礎(chǔ)。
    
    查詢表達(dá)式能夠在語(yǔ)言中使用類似SQL或者XPath/XQuery的語(yǔ)法進(jìn)行數(shù)據(jù)查詢。
    
    查詢表達(dá)式以from子句開始,以select或者group子句結(jié)束。大致組成順序是(可選):from子句、where子句、orderby子句、select子句和where子句以及into子句。
    
    看一個(gè)例子:
    string[] Weekdays = {
        "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"
    };
    IEnumerable<string> enum_day =
        from day in Weekdays
        where day[0] == 'T'
        orderby day
        select day.ToUpper();
    foreach(var s in enum_day){
        Console.WriteLine(s);
    }
    你將會(huì)得到:
    THURSDAY
    TUESDAY
    
    如何?Amazing!
    
    那么,這樣的語(yǔ)句為什么能夠編譯呢?
    其實(shí),編譯器在編譯的時(shí)候進(jìn)行了轉(zhuǎn)換。上例中的enum_day的定義部分可轉(zhuǎn)換為:
    IEnumerable<string> enum_day = Weekdays
        .Where(day => day[0] == 'T')
        .OrderBy(day => day)
        .Select(day => day.ToUpper());
    這樣就清楚了吧(“括號(hào)里面的表達(dá)式什么?”不會(huì)吧,復(fù)習(xí)一下Lambda表達(dá)式吧)。
    
    當(dāng)然了,像SQL這樣的語(yǔ)句進(jìn)行嵌套、組合等可以產(chǎn)生很多有趣的查詢方法,C# 3.0中的查詢方式雖然僅是簡(jiǎn)化版的SQL語(yǔ)法,不過(guò)經(jīng)過(guò)嵌套、組合等方法當(dāng)然可以形成復(fù)雜的表達(dá)式,這就靠大家今后鞏固了

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多