2012年7月24日 星期二

LINQ 的隱含型別、延後執行

這篇只是從實作使用LINQ查詢陣列(Array)中的偶數有哪些 (LINQ TO Object),過程中體驗一下LINQ的二個特點:「隱匿型別」和「延後執行」

◎以上程式範例LINQ2.aspx,如在頁框下不能操作,請開新視窗操作

    Protected Sub Page_Load(sender As Object, e As System.EventArgs) Handles Me.Load
        Me.Label1.Text = ""
        Me.Label2.Text = ""
        Me.Button1.Text = "查詢陣列中的偶數有哪些?"
        Me.Button2.Text = "查詢陣列中的偶數有哪些?(故意在陣列中加string)"
        Me.Button3.Text = "查詢陣列中的偶數有哪些?(在執行查詢前更改陣列中的值)"
    End Sub
    Protected Sub Button1_Click(sender As Object, e As System.EventArgs) Handles Button1.Click
        ' 下面這句定義一個陣列並沒有設定型別,不過編輯器(compiler)會利用區域型別推斷(local type inference)自已推斷出來
        ' 不信的話,點選Qcollection(),你可以看到「....As Integer」的提示
        ' 如果要自行設定不交給編輯器推斷的話也可以
        ' Dim Qcollection() As Integer = {1, 2, 3, 4, 5, 6}
        Dim Qcollection() = {1, 2, 3, 4, 5, 6}

        ' 下面的定義查詢所使用到的變數Qquery,必須是可查詢的型別
        ' 是不是可查詢的型別?這個不用自已想
        ' 以本例的Qqery,編輯器會推斷他是..IEnumerable(Of Integer)
        ' 同樣的,Qelement 的型別也不用明確宣告…
        ' from… in…本身就是個迴圈,但是在這個階段還不會「實際」運算
        Dim Qquery = From Qelement In Qcollection
                     Where Qelement Mod 2 = 0
                     Select Qelement

        ' 如果有一系列值,則可以在 For Each 迴圈中使用反覆運算變數 (在這個範例中是Qelement) 來存取擷取到的資料
        ' 上面的變數 Qquery 只是保存「查詢定義」
        ' 特別注意,執行階段的變數Qelement和定義查詢階段的變數Qelement,
        ' 名稱一樣(我故意的,因為覺得他們本質上本來就一樣),
        ' 但是是不同的範圍所以也不會有變數名稱重複的衝突
        For Each Qelement In Qquery
            Me.Label1.Text &= Qelement & ","
        Next

        ' 這段只是應用IEnumerable(T) 的擴充方法(Extension Methods)
        ' 因為 Qquery 受型別推斷是支援IEnumerable(T)
        ' 所以自然繼承使用它的方法
        Dim Qresult = Qquery.Count
        Me.Label2.Text = "結果查詢到" & Qresult & "筆資料"
    End Sub

    Protected Sub Button2_Click(sender As Object, e As System.EventArgs) Handles Button2.Click
        ' 這邊在定義陣列時故意不給型別,然後在陣列序列中加一個字串
        ' 這個時候編輯器的型別推斷會認為他是物件

        Dim Qcollection() = {1, 2, "小明", 3, 4, 5, 6}

        ' 點選下面的Qquery看看,編輯器會因無法推斷
        ' 就假設 Qqery是IEnumerable(Of Object)
        ' IEnumerable(T) 的原因是因為陣列本來就是有IEnumerable(T)
        ' of object 是因為上面Qcollection已經被推斷為object
        ' 後面的Qelement也跟著以為自已是object
        ' 所以這個程序還不用寫到執行階段,設計上就出錯了
        ' 但是因為是延後執行的特性,所以你試著按這個按鈕,並不會報錯喔
        ' 因為Qquery 只負責儲存「定義」而已,還沒執行
        Dim Qquery = From Qelement In Qcollection
                     Where Qelement Mod 2 = 0
                     Select Qelement
    End Sub
    Protected Sub Button3_Click(sender As Object, e As System.EventArgs) Handles Button3.Click
        Dim Qcollection() = {1, 2, 3, 4, 5, 6}
        Dim Qquery = From Qelement In Qcollection
                     Where Qelement Mod 2 = 0
                     Select Qelement
        For Each Qelement In Qquery
            Me.Label1.Text &= Qelement & ","
        Next

        ' 前面定義名稱不變,改變陣列的序列值
        ' 底下把原陣列中的第1個序列值改成12,第2個序列值改成4
        ' 這邊再用一個迴圈來執行查詢
        ' 因為「延後執行」的特性
        ' 則可以在 For Each 迴圈中使用反覆運算變數
        Qcollection(1) = 12
        Qcollection(2) = 4
        For Each Qelement In Qquery
            Me.Label2.Text &= Qelement & ","
        Next
    End Sub

談談隱含型別、匿名型別:

有人是稱它叫隱匿型別,應該是叫隱「含」比較妥當,這個隱含型別,就我的認知應該是,我們沒有給一個物件相對的型別,但在編輯器運作時需要運算,必須掌握每個物件的確切型別,在我們不主動提供的情況下,由系統的型別推斷器來強行推定。

在寫LINQ的時候我自已感覺,在查詢的過程中會大量使用變數,正常來說,定義變數除了提供名稱外,還要自已列出型別,這樣子有助於程式「可讀性」。不過我們也可以不用「寫出來」,透過型別推斷器,等號"="右邊的值會告訴推斷器這個變數應該是什麼型別…這就完成了隱含型別,也就是說,物件本身是「有型別」的喔。

如果你覺得這會影響程式的可讀性,那麼我告訴你,在Visual Studio裡好像沒什麼差,有時候反而可以讓程式看起來簡潔些,想知道變數隱含的型別,滑鼠點物件就看到ToolTip了,如下圖:
image

講到隱含型別,有一個匿名型別常常拿來被比較,匿名型別的相對是具名型別,這個範例上並沒有相關的範例,匿名型別對我來說,是「不具有型別」的。

可以看看我從上一篇的LINQ三部曲…用基本查詢三步驟查詢三大資料來源裡寫的LINQ TO SQL 的範例,行35-37中,為了讓資料塞到GridView裡,我必須要明確的指定每個欄位值的型別…萬一我要設計的GridView欄位達到上百個,那麼我除了要自已判斷型別外,還要手動撰寫。那麼開發的時程就相對加長了許多,如果所有的欄位型別都交由型別推斷器來協助判斷並給予該物件一個隱含的型別就能省去不少力氣了。

但關鍵是連我自已都不知道我提取出來的欄位值到底是什麼,換句話說,我連這個欄位「等於」什麼值,這等號右邊的值都不給型別推斷器,它要怎麼推算?所以這個時候乾脆在物件形成前先不給他型別具名,等到物件形成後再讓推斷器去計算了。

這個就是我認知的匿名型別。

所以隱含型別和匿名型別是不一樣的東西。(應該…= =)


偶有所得

寫linq的時候突然覺得在定義資料來源、定義查詢方法、定義執行查詢的過程中用了好多變數,定義變數的時候如果亂用一些名稱的話,一開始為了程式「可讀性」,會用「功能(用途)加型別」的方式來命名變數,但寫愈來愈多的LINQ後發現,以後要重複使用時就會浪費很多時間在重新命名上,在實作過程體驗了所謂隱含型別、匿名型別後,加上以前學的e參數(詳情見文章GridView中和「刪除」事件有關的事件常式),我也開始覺得程式「可讀性」可以用dictionary的方式來設計就可以了,不一定要hard-coded,所以正在努力增加往後寫LINQ時的「可及性」。以後如果有LINQ實作心得的話,也會朝這個方向來作。


It’s my life live love …

妹妹剪短髮以後應該會像哥哥…還好有髮型撐著妹妹的性別
妹妹剪短髮以後應該會像哥哥…還好有髮型撐著妹妹的性別
倒是哥哥常常被人講眼睫毛很長…比較像女生
倒是哥哥常常被人講眼睫毛很長…比較像女生

沒有留言:

張貼留言

Related Posts Plugin for WordPress, Blogger...
// Dnow Function