顯示具有 Visual Basic 標籤的文章。 顯示所有文章
顯示具有 Visual Basic 標籤的文章。 顯示所有文章

集合與泛型集合中Contains方法處理效能測試

Contains方法

Contains()方法在很多地方都可以使用,主要是用來比較指定物件內是否含有指定的項目。例如:String 型別的 Contains()方法就經常拿來搜尋或過濾字串使用。

    Dim YesNo As Boolean = "Bruce很帥XD".Contains("帥")
    Console.WriteLine(If(YesNo, "帥", "不帥"))
    Console.ReadLine()
   

但今天想瞭解在含有大量資料時,集合與泛型集合內的Contains方法的處理速度。

測試原因

想瞭解與測試的原因是因為亂馬客寫了一篇 .NET]產生不重覆的數值(List vs HashSet) 的文章,他測試結果是正確的,不過我認為測試範圍可以加大到集合與泛型集合,所以有了以下測試程式碼與測試結果。程式碼參考亂馬客文章進行修改,主要加入了 Counter 變數,記錄下 while 迴圈的執行次數,這樣比較能瞭解誤差值。

集合與泛型集合 - Contains方法

程式碼主要是要產生不重覆的數值,所以重點其實只在 while 那一行程式碼,這裡我加大數值的量,由 1 ~ 100000 裡取出不重覆的 90000 個數值。因為我們只單存取一個數值,所以 Key/Value 的集合與泛型集合就不在測試程式碼之內。

ASP.NET Web API 心得筆記 (9) 簡介 System.Net.Http.HttpClient 類別

HttpClient 類別介紹

提供基礎類別來傳送 HTTP requests(請求)接收 HTTP responses(回應)從一個 URI 的資源定義。目前 HttpClient 類別為 .NET Framework 4.5 Beta 中 System.Net.Http 命名空間之下,使用方式在未來正式版中可能會有修改,以下所提範例在未來是否能正常運作是未知,但提供的觀念我相信是一樣的。

HttpClient 類別使用說明

HttpClient 類別執行個體做為發送的 HTTP 請求的會話(session),HttpClient 執行個體是應用於該執行個體來執行所有請求的設定的集合。此外,每一個 HttpClient 執行個體將使用自己的連接池(connection pool),以隔離來自其他 HttpClient 執行個體所執行的請求的請求。

使用 HttpClient 類別非同步取得 URI 資源

我們先看一個最簡單的範例,我們開一個【主控台應用程式】:

.NET Framework 垃圾收集(garbage collection)初了解

感謝 91哥提供的兩篇文章,讓我更加了解垃圾收集 (garbage collection,GC),看完感覺,越自動化感覺不到它的存在越是重要


稱之為入門篇,因為他寫的真的很好懂,以下實 C# 轉 VB,再加上自己的一些註解。

Module Module1

    Private Sub GenerationDemo()
        ' 了解 GC 最大為 0, 1, 2 三層
        Console.WriteLine("最大 GC 代: {0} ", GC.MaxGeneration)

        ' Heap 新增一物件
        Dim obj As New GenObj("Generation")

        ' 顯示目前物件在 GC 第幾層
        obj.DisplayGeneration()

        For i As Integer = 1 To GC.MaxGeneration
            ' 進行 GC 收集動作
            GC.Collect()
            ' 因為 obj 沒被回收
            ' 每次會被拉高一層代
            obj.DisplayGeneration()
        Next

        obj = Nothing

        For i As Integer = 0 To GC.MaxGeneration
            ' 強制立即執行層代零至指定層代的記憶體回收。
            GC.Collect(i)
            ' 暫止目前的執行緒,直到處理完成項佇列的執行緒已經清空該佇列為止。
            GC.WaitForPendingFinalizers()
        Next

        Console.WriteLine("Demo Stop.")
    End Sub

    Sub Main()
        GenerationDemo()
        Console.ReadLine()
    End Sub

End Module

Class GenObj
    Private _name As String

    Sub New(name As String)
        name = _name
    End Sub

    ' 回傳目前物件所在 GC 第幾層
    Sub DisplayGeneration()
        Console.WriteLine("我在第 {0} 層", GC.GetGeneration(Me))
    End Sub
End Class

整個過程如同程式流程一樣:
  1. 取得目前 GC 層數
  2. 新增一物件,並顯示此物件目前所在 GC 層數
  3. 進行 GC 回收,因為每次 GC回收 obj物件沒有消失,代表重要性需要提升,加高一層級
  4. 設定 obj 為 Nothing
  5. 執行 GC 回收,GC 發現 obj 為 Nothing,回收此資源

此篇說明比較學術,但可以更細部了解整個 GC 運作,真的是大內高手專欄。

個人心得

我覺的看完第一篇就很有滿足感,就我在 Web Developer 的工作內容上,我們一般只用用 Close 或 Dispose ,很少會去手動碰 GC 的內容。不過,能更深一層的了解 .NET Framework 是有其必要性。

Visual Basic 11 的 Iterator 與 Yield 心得筆記(5)

最後我們來了解整個 Iterator 與 Yield 執行流程。我們以心得(2)裡的範例來進行解釋。

    Private Iterator Function EvenSequence(firstNumber As Integer,
                                           lastNumber As Integer) As IEnumerable(Of Integer)
        ' 回傳偶數
        For number As Integer = firstNumber To lastNumber
            If number Mod 2 = 0 Then
                Yield number
            End If
        Next
    End Function

Iterator 我們前面提過,主要是要跟 compiler 說這是個要能列舉的函式,幫我實作 MoveNext、Current…等方法。那 Yield 呢?其實你在 Yeild 下【中斷點】去執行看看,就差不多知道結果了。

當程式執行到 Yield 陳述式時,便會儲存目前的位置,下一次呼叫 Iterator 時,便會從這個位置重新開始執行。

回想一下前面跑過的所有範例,我們每次跑 For ~ Each 迴圈,Yield 陳述式在做什麼事,記位置、回傳值,直到反覆運算(Iterator)動作結束(即 For ~ Each 每次取一個值,取完為止。)。記住這一次的位置,回傳這一位置的值,等下次再執行至 Yield 時,就跳至下一個位置,然後又是記住這一次的位置,回傳這一位置的值…重覆到結束。其實沒有那麼難懂。

雖然 C# 的使用者可能已經用到不想用了,但能在 Visual Basic 11 裡看到 Iterator 與 Yield 實在太高興了,也希望未來 C# / VB 之間能同步化。

Visual Basic 11 的 Iterator 與 Yield 心得筆記(4)

Try ~ Catch與 Iterator ~ Yield

程式裡不可能沒有 Try ~ Catch,我們看看 Iterator 與 Yield 在 Try ~ Catch 裡的使用。

Module Module1
    ''' <summary>
    ''' 測試 Iterator 與 Yield 與 Try ~ Catch
    ''' </summary>
    Private Iterator Function TestTryCatch() As IEnumerable(Of Integer)
        Try
            Yield 3
            Yield 4
            Throw New Exception("出事了!")
            Yield 5
            Yield 6
        Catch ex As Exception
            Console.WriteLine(ex.Message)
        Finally
            Console.WriteLine("Finally 已被執行!")
        End Try
    End Function

    Sub Main()
        For Each number As Integer In TestTryCatch()
            Console.WriteLine(number)
        Next
        Console.ReadLine()

        ' 輸出:
        ' 3
        ' 4
        ' 出事了!
        ' Finally 已被執行!
    End Sub
End Module

你可以發現,執行至 Throw New Exception() 之後,因為進入的 Catch 然後執行至 Finally,程式就結束。也就是說,當程式執行至一半擲出例外(Catch),整個 Iterator Function 就會停止不在進行。而 Finally 是不管有沒有發生例外,都還是會被執行。這很符合我們的常理。

匿名方法(Anonymous Methods)與 Iterator ~ Yield

Module Module1
    Sub Main()
        Dim iterateSequence = Iterator Function() As IEnumerable(Of Integer)
                                  Yield 1
                                  Yield 2
                                  Yield 3
                              End Function

        ' 呼叫匿名方法
        For Each number As Integer In iterateSequence()
            Console.Write(number & " ")
        Next
        Console.ReadLine()

        ' 輸出: 1 2 3
    End Sub
End Module

與之前差異不大,透過 Iterator 與 Yield 的幫忙,讓匿名方法可以擁有舉列的能力。我們再舉一個範例:

Module Module1
    ''' <summary>
    ''' 取得數字的順序
    ''' </summary>
    ''' <param name="low">最小不得小於1</param>
    ''' <param name="high">最大不得大於100</param>
    ''' <returns>有順序數字</returns>
    Function GetSequence(low As Integer,
                         high As Integer) As IEnumerable

        ' 驗證參數
        If low <= 1 Then
            Throw New ArgumentException("low is too low")
        End If

        If high >= 100 Then
            Throw New ArgumentException("high is too high")
        End If

        ' 透過可舉列的匿名方法建立順序
        Dim iterateSequence = Iterator Function() As IEnumerable
                                  For index = low To high
                                      Yield index
                                  Next
                              End Function

        Return iterateSequence()
    End Function

    Sub Main()
        For Each number As Integer In GetSequence(38, 43)
            Console.Write(number & " ")
        Next
        Console.ReadLine()

        ' 輸出: 38 39 40 41 42 43
    End Sub
End Module

這裡注意驗證參數的部份,如果你把驗證參數寫在匿名方法裡,那必須等到 For Each 第一次去呼叫 Iterator 時,才會開始發生作用。在 Iterator 的說明文件中有一段話:「Iterators 是LINQ查詢中延後執行行為的基礎。」又學到一招。【註】在 MSDN 說明中「在 C# 裡 yield 陳述式不能出現於匿名方法中。」

泛型集合(Generic List)與 Iterator ~ Yield

下面這個範例,是 Stack(Of T)泛型類別實作 IEnumerable(Of T)泛型介面。

Visual Basic 11 的 Iterator 與 Yield 心得筆記(3)

以下範例會實作一個 Zoo 類別,此類別裡可儲存 Animal 私有類別資料,資料很簡單,就名稱與分類,分類只有簡單兩種,鳥類與哺乳類。讓我們看看在類別裡如何使用 Iterator 與 Yield。

''' <summary>
''' Zoo 類別,可存放哺乳類與鳥類資料
''' </summary>
Class Zoo
    Implements IEnumerable

    ''' <summary>
    ''' 私有 Animal類別,有名稱和分類兩個屬性
    ''' </summary>
    Private Class Animal
        Public Property Name As String
        Public Property Type As TypeEnum

        ' 簡單分類
        Public Enum TypeEnum
            Bird ' 鳥類
            Mammal ' 哺乳類
        End Enum
    End Class

    Private animals As New List(Of Animal)

    ''' <summary>
    ''' 新增哺乳類
    ''' </summary>
    ''' <param name="name">名稱</param>
    Sub AddMammal(ByVal name As String)
        Dim thisAnimal As New Animal() With {.Name = name,
                                            .Type = Animal.TypeEnum.Mammal}
        animals.Add(thisAnimal)
    End Sub

    ''' <summary>
    ''' 新增鳥類
    ''' </summary>
    ''' <param name="name">名稱</param>
    Sub AddBird(ByVal name As String)
        Dim thisAnimal As New Animal() With {.Name = name,
                                            .Type = Animal.TypeEnum.Bird}
        animals.Add(thisAnimal)
    End Sub

    ' 實作 GetEnumerator 方法
    Iterator Function GetEnumerator() As IEnumerator Implements _
                                         IEnumerable.GetEnumerator

        ' 回傳列舉值
        For Each theAnimal As Animal In animals
            Yield theAnimal.Name
        Next
    End Function

    ' 唯讀,回傳哺乳類
    Public ReadOnly Property Mammals As IEnumerable
        Get
            Return AnimalsForType(Animal.TypeEnum.Mammal)
        End Get
    End Property

    ' 唯讀,回傳鳥類
    Public ReadOnly Property Birds As IEnumerable
        Get
            Return AnimalsForType(Animal.TypeEnum.Bird)
        End Get
    End Property

    ' 舉列動物分類
    Private Iterator Function AnimalsForType(type As Animal.TypeEnum) As IEnumerable
        For Each theAnimal As Animal In animals
            ' 找出我們要的分類
            If (theAnimal.Type = type) Then
                Yield theAnimal.Name
            End If
        Next
    End Function
End Class

第一個重點,前面心得已經提過幾次,我們必須實作 IEnumerable.GetEnumerator ,也就是它讓我們有的型別有列舉的能力。第二個重點,AnimalsForType()方法,等一下希望能直接透過動物分類去找出目前此分類中有那些動物,因此我們實作了一個 Iterator Function 來幫忙。

我們看主程式如何呼叫:

Visual Basic 11 的 Iterator 與 Yield 心得筆記(2)

前一篇「Iterator 與 Yield 心得(1)」我們了解了 IEnumerable介面與 IEnumerator介面的關係,實作 IEnumerable介面的 GetEnumrator 方法可使型別變成可列舉的型別,還必須實作 IEnumerator介面裡的 MoveNext、Current、Reset等。

現在透過 VB 11 終於也可以有 C# 的方便性。使用 Iterator 與 Yield 我們也可以很方便實作出可舉列的型別或方法。我們先了解如何撰寫 Iterator 與 Yield 程式碼,然後在解釋整個 Iterator 與 Yield 的運作流程。

Module Module1
    Sub Main()
        Dim number As Integer = 0
        For Each number In SomeNumbers()
            Console.Write(number & " ")
        Next
        Console.ReadLine()

        ' 輸出:3 5 8
    End Sub

    ''' <summary>
    ''' Iterator 函式
    ''' </summary>
    Private Iterator Function SomeNumbers() As IEnumerable
        ' 使用多個 yield 關鍵字
        Yield 3
        Yield 5
        Yield 8
    End Function
End Module

我們來看 SomeNumbers():
  1. 在 Function 之前,必須加上 Iterator 關鍵字
  2. 回傳值使用 Yield 關鍵字,而非 Return
  3. C# 使用 yield break 陳述式來結束 Iterator,VB 11 使用 Exit Function 或 Return 來結束
  4. C# 使用 yield return,VB 11 直接使用 Yield
  5. Iterator 的回傳型別只能是 IEnumerable、IEnumerable(Of T)、IEnumerator、IEnumrator(Of T) 四種。
是否發現在使用 SomeNumbers() 時,你並不需要去實作整個 IEnumerable介面內容了。那是當 compiler 偵測到 Iterator 時,它會自動產生 IEnumerable 或 IEnumerable(Of T) 介面的 MoveNext、Current 注意的是,它不會產生 Reset 方法

我們來看在泛型裡的使用。

Visual Basic 11 的 Iterator 與 Yield 心得筆記(1)

在前一篇「Visual Basic 11 Beta 新功能介紹」我有提到一個 Iterator 與 Yield,這個 Iterator 與 Yield 是 C# 2.0 (Visual Studio 2005)提供的功能,它提供了簡易實作出【反覆運算】函式的功能,換成白話一點的說法,您想想,如果您想讓您的類別或函式能讓 For Each 使用,您應該怎麼做?

說來簡單,您只需實作整個 IEnumerable 介面IEnumerable(Of T) 介面。我們以MSDN中 IEnumerable 介面 範例來說明 IEnumerable 介面實作。

Imports System
Imports System.Collections

''' <summary>
''' Person 類例
''' </summary>
Public Class Person

    Public firstName As String
    Public lastName As String

    ''' <summary>
    ''' 建構式
    ''' </summary>
    ''' <param name="fName">姓</param>
    ''' <param name="lName">名子</param>
    Sub New(ByVal fName As String, ByVal lName As String)
        Me.firstName = fName
        Me.lastName = lName
    End Sub
End Class

首先,宣告一個Class Person(個人),裡面只有兩個屬性,firstName 和 lastName,並且了一個簡單的初始化建構式。

''' <summary>
''' People 類別
''' </summary>
''' <remarks>實作 IEnumerable</remarks>
Public Class People
    Implements IEnumerable

    ' 私有 _person()陣列變數,存放 Person類別資料
    Private _people() As Person

    ''' <summary>
    ''' 建構式
    ''' </summary>
    ''' <param name="pArray">Person 陣列</param>
    Sub New(pArray() As Person)
        ' 初始化陣列物件
        _people = New Person(pArray.Length - 1) {}
        
        ' 儲存至私有 _person()陣列變數
        Dim i As Integer
        For i = 0 To pArray.Length - 1
            ' 將傳入物件陣列儲存在 _people陣列
            _people(i) = pArray(i)
        Next i
    End Sub

    ''' <summary>
    ''' 實作 IEnumerable.GetEnumerator 方法
    ''' </summary>
    ''' <returns>列舉 _people()陣列的結果</returns>
    Function GetEnumerator() As IEnumerator _
                     Implements IEnumerable.GetEnumerator

        ' 回傳列舉 _people陣列結果
        Return New PeopleEnum(_people)
    End Function
End Class

宣告一個Class People(人),重點在第二行,它實作了 IEnumerable介面,當您輸入完 Implements IEnumerable 按下 Enter 後,會自動跳出需實作的函式,即 GetEnumerator() 函式。People類別主要是要存放 Person(個人)類別資料,存放於陣列之中,然實透過實作 GetEnumerator() 函式,讓 People類別擁有可反覆運作的能力。在 GetEnumerator() 函式中,我們還需要透過一個 PeopleEnum類別來幫忙完成最後一件事。

''' <summary>
''' 列舉 People 類別
''' </summary>
''' <remarks>實作 IEnumerable</remarks>
Class PeopleEnum
    Implements IEnumerator

    Public _people() As Person

    ' 在第一個列舉之前的位置
    ' 直到第一次呼叫 MoveNext()
    Dim position As Integer = -1

    ''' <summary>
    ''' 建構式,初始化 _people() 陣列
    ''' </summary>
    ''' <param name="list">Person陣列</param>
    Sub New(list() As Person)
        _people = list
    End Sub

    ''' <summary>
    ''' 實作 MoveNext() 函式
    ''' </summary>
    ''' <returns></returns>
    ''' <remarks></remarks>
    Function MoveNext() As Boolean _
                    Implements IEnumerator.MoveNext

        position = position + 1
        Return (position < _people.Length)
    End Function

    ''' <summary>
    ''' 實作 Reset() 副程式
    ''' </summary>
    Sub Reset() Implements IEnumerator.Reset
        position = -1
    End Sub

    ''' <summary>
    ''' 實作唯讀 Current() 屬性
    ''' </summary>
    ''' <value></value>
    ''' <returns>_people陣列值</returns>
    ''' <remarks></remarks>
    Public ReadOnly Property Current() As Object _
                    Implements IEnumerator.Current
        Get
            Try
                ' 回傳現在位置的 _people陣列值
                Return _people(position)
            Catch ex As IndexOutOfRangeException
                Throw New InvalidOperationException()
            End Try
        End Get
    End Property
End Class

PeopleEnum類別,很明顯是在進行列舉用的類別。

Visual Basic 11 Beta 新功能介紹

隨著 Visual Studio 11 Beta 的推出,Visual Basic 11 Beta 也正式推出,讓我們來看看有什麼Visual Basic 11 Beta裡有什麼新內容吧!

非同步程式, Asynchronous Programming


現在要在VB11裡開發非同步程式(Asynchronous Programming)變的很簡單了。在VB11中新增 Async / Await 關鍵字。

範例程式來源:101 Samples in C# and Visual Basic

VB 10: await - Serial Network Requests

Public Sub AsyncIntroSerialBefore()
    Dim client As New WebClient()

    AddHandler client.DownloadStringCompleted, AddressOf AsyncIntroSerialBefore_DownloadStringCompleted_1
    client.DownloadStringAsync(New Uri("http://www.weather.gov"))
End Sub

Sub AsyncIntroSerialBefore_DownloadStringCompleted_1(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs)
    WriteLinePageTitle(e.Result)

    Dim client As New WebClient()

    AddHandler client.DownloadStringCompleted, AddressOf AsyncIntroSerialBefore_DownloadStringCompleted_2
    client.DownloadStringAsync(New Uri("http://www.weather.gov/climate/"))
End Sub

Sub AsyncIntroSerialBefore_DownloadStringCompleted_2(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs)
    WriteLinePageTitle(e.Result)

    Dim client As New WebClient()

    AddHandler client.DownloadStringCompleted, AddressOf AsyncIntroSerialBefore_DownloadStringCompleted_3
    client.DownloadStringAsync(New Uri("http://www.weather.gov/rss/"))
End Sub

Sub AsyncIntroSerialBefore_DownloadStringCompleted_3(ByVal sender As Object, ByVal e As DownloadStringCompletedEventArgs)
    WriteLinePageTitle(e.Result)
End Sub

VB 11: await - Serial Network Requests

Public Async Sub AsyncIntroSerial()
    Dim client As New WebClient()

    WriteLinePageTitle(Await client.DownloadStringTaskAsync(New Uri("http://www.weather.gov")))
    WriteLinePageTitle(Await client.DownloadStringTaskAsync(New Uri("http://www.weather.gov/climate/")))
    WriteLinePageTitle(Await client.DownloadStringTaskAsync(New Uri("http://www.weather.gov/rss/")))
End Sub

如果要在要 Visual Studio 2010 for Visual Basic 10 環境下去使用 Async / Await 關鍵字,必須去安裝 Visual Studio Async CTP (Version 3) 套件。當然,也支援非同步的除錯及單元測試(MSTest與xUnit支援良好)。

請參考:

Iterators

Iterator 首先出現在 C#,在 VB 10 之前未與 C# 同步使用此方法,但在 VB 11 裡終於可以 C# 同步使用。想了解 Iterator 方法,我想最好先從 Iterator (C# 程式設計手冊) 下手,因為 VB10 之前沒有 Iterator 方法,所以去看 C# 的資料在所難免。其實之前我根本沒有注意過 Inerator 方法,直到在 MVC 3 for C# 的程式碼中,看到有人使用 Iterator 方法,結果我怎麼轉就是無法在 VB 實作、執行,後來才發現原來 VB 10 根本就無此方法,這讓我非常頭痛。

在 Visual Studio 11 開一個 Console Application ,輸入以下測試程式:

Windows 8 CP Beta / Visual Studio 11 Beta 安裝經驗

隨著 Windows 8 Consumer Preview BetaVisual Studio 11 Beta 的推出,相信離正式版已經不遠了,Beta 與 Preview 不同,Preview 只是讓你先看看,這一次的 Beta 可說是 UI 大改版,與之前的 Preview 差異非常大。

Windows 8 Consumer Preview Beta and Visual Studio 11 Beta
圖一:Windows 8 Consumer Preview Beta and Visual Studio 11 Beta

在這裡要先分享一個個人悲慘的經驗,在去年 Windows 8 Preview / Visual Studio 11 Preview / ... Preview 推出時,第一時間就去下載,記得由於當時主機上的 RAM 不足,而且看了看文件,說可以把 Visual Studio 11 Preview 和 Visual Studio 2010 安裝在一起而不會有問題,就相信的給它裝下去。整體而言,除了整合更多好用延伸工具進 VS 11 Preview,VS 11 Preview 與 VS 2010 差異不大。當然,期間重要的是要玩 ASP.NET MVC 4 Preivew。

這一次可以說是大大【Beta 週】(從 2012/2/29 ~),上從作業系統的 Beta,下至開發工具 Beta,連 IIS 7.5 Express 此類工具也都開始更新,當然還有 ASP.NET MVC 4 Beta。ASP.NET MVC 4 Beta 在 asp.net 上只有兩個選擇,一是使用 Web PI 安裝,二是下載安裝檔安裝,但我就是裝不起來,而且從錯誤訊息看來,是因為我有安裝 VS 11 Preview / .NET 4.5 Preview 的關係。

找到問題點,那就比較好處理,進行了一下午的 uninstall VS 11 Preview / .NET 4.5 Preview / ... Preview 的作業,移除完畢問題還是沒有解決,更慘的是,我連 Visual Studio 2010 都打不開了,使用【修復】還是處理不了,最後下了一個決定:【重灌電腦】。

最後得到一個結論,這些 Preview / Beta 的內容還是在測試機(VM Host)裡玩玩就好,除非你有空主機,或主機有進行 Ghost 之類的備份,不然還是不要太相信…它的話。

Windows 8 Consumer Preview Beta 安裝經驗


安裝只有兩個步驟,一,問安裝至那個 Disk,二,問產品金鑰。安裝完成後會問你是要整合【Windows Live Account】或【建本機帳號】,選擇後,就可以開始測試 Windows 8 Consumer Preview Beta。

由 Windows 8 CP Beta 的安裝經驗來說,是非常不錯,過程沒有太多干擾,就安裝而言,每一代的 Windows 都提升不少方便性,而且安裝速度也算不錯。8 > 7 > Vista > XP > ME > 98 > 95 > 3.1。

Visual Studio 11 Beta 安裝經驗


Visual Studio 11 Beta 預設為完全安裝,所以從頭到尾只有按兩次Click,一次 Install,一次 Launch,因為安裝的內容不少,可以看到 CPU 與 Disk I/O 有些小忙,也因為沒有什麼可以介入的地方,會等的有些無聊(或是說順)。

Visual Studio 11 Beta 整個 UI 大改版,不過這不是重點。原來像 ASP.NET MVC 4 Beta 等測試環境都已經內建於 Visual Studio 11 Beta 之中,難怪 asp.net 網站只放了 Visual Studio 2010 版本的 ASP.NET MVC 4 Beta 的安裝檔。

2012 A Best Present "Microsoft MVP Award"

我只是一個平凡人,如果我能,你也能。by KKBruce
首先,我一定要感謝我最愛的家人們,不管什麼時候、什麼事,你們都永遠支持我。再來感謝所有幫助過我的人,因為有你們的表率,讓我有學習的對象,讓我有機會成為一位Microsoft MVP。

2012年最棒的一份新年禮物,那就是我當選2012年 Microsoft MVP

KKBruce Microsoft MVP獎項
圖一:Microsoft MVP獎項
KKBruce 2012 Microsoft MVP獎盃
圖二:2012 Microsoft MVP獎盃

Microsoft MVP得獎感言

我不知道,我是不是很「感覺」的人?為什麼說很感覺呢?從開啟這個KingKong Bruce記事已經快滿5年的時間,除了一些在生活上的感想外,主要有很大一部分是以技術為主的文章內容,在學習這些以Microsoft為主的技術路上,常常覺得這些Microsoft MVP對於技術出神入化,只有嘆為觀止可以形容,常常有那種抬頭看天的感覺,人就是那麼奇怪,想追求平凡又不甘於平凡,我給自己一個夢想,想像有一天我也能成為一位Microsoft MVP

從有那個夢開始,我慢慢觀察網路上這些Microsoft MVP,我發現我錯了,這些Microsoft MVP們除有擁有技術外,更重要的是有一顆幫助別人的心,你很難想像,一位未曾謀面Microsoft MVP,使用MSN從頭到尾只和你聊"程式",有空還會問你說,最近還有沒有什麼新鮮貨(新文章),害得我想不長進都不行。

還有,當你看到突然有Microsoft MVP跑來你的Blog上指導時,我心想,我只是個無名小卒,怎會讓這些大腳、起大腳(台)的Microsoft MVP願意來我的Blog上指導我,意外之外,我了解到技術非第一,還是那句「有一顆助人的心」。

去年年底,不知道為什麼,我很想、非常想、超級想試試看去申請Microsoft MVP,我就在MSN上問Allen大大一些關於Microsoft MVP資格問題,原來申請Microsoft MVP有二種方式:
  • 毛遂自薦
  • 他人推薦
我是自己寫信去 mvpga@microsoft.com 要申請表格來填寫,不過好玩的事是幾天後,我在Plurk上收到一則私人息訊,發訊者是現任Microsoft MVP 91哥,他問我要不要去申請Microsoft MVP,我又嚇到了,這些Microsoft MVP的通靈能力也太強了吧!我又沒和任何人提過我想申請Microsoft MVP,感謝之餘,更讓我確定要去申請這個Microsoft MVP獎項。

在我寄送出申請表格之後的幾週後,有一天我的E-Mail裡寄來一封信,怎麼是Microsoft MVP的申請表格,一看,原來是我的老師MISLab2000寄來的,我很不意思說我已經寄出申請表格了,不過我心想,看來我的感覺是對的,就是這一次。

身體健康最重要

我要在得獎感言最後寫得病的心路歷程。可以得這個獎,我還感謝我的病「甲狀腺機能亢進」。

約半年多前的幾個月,我變成了超人,因為經常性失眠,這個失眠不是睡不著,而是睡一下下就睡飽了,天未亮雞未啼人已起,起來沒事做,就坐在電腦前看看資料、寫寫文章,所以今年文章的產量非常不錯。

因為平常坐得久,都有在小心控制飲食,就在發病的那一個月裡,我突然廋了十三公斤之多,我才在想怎麼這個月的效果超好,可以把心得拿來出書賺點奶粉錢,就在幾天後的早上,我帶著小花去散步,才走沒多久就覺得不舒服,回到家已經是臉色慘白加喘不過氣。

那個月的平均心跳在140上下,常常感到累而就在我家地板上睡著,選擇在地板上睡是因為很熱,心跳快加上新陳代謝也快,所以那一二個月的時間,我都無法在床上睡覺,醫生說,我那時的新陳代謝就算坐著不動,也是平常人的三~四倍,難怪會廋那麼快。

這讓我想到一位Visual Basic的前輩王國榮,我們那個年代,大概學Visual Basic的沒有一位不拜讀他的作品,後來好像生病引退了。

追求不平凡,要有健康的身體。

申請Microsoft MVP參考資料

  1. 什麼是微軟最有價值專家?
  2. 申請Microsoft MVP報名表
  3. Microsoft MVP常見問題集

ASP.NET MVC - Visual Baisc QRCode擴充方法

以下範例程式修改於阿源哥哥「使用ASP.NET MVC 3產生二維條碼」一文,除將原文之C# Code轉換為Visual Basic Code,還進行
  • alt屬性定義為必填
  • 更完整的註解說明
以產生更符合 SEO 的 HTML img 標籤。相關說明都寫在程式註解中,就不在多做解釋。

QRCodeExtension.vb - Visual Baisc QRCode擴充方法


Imports System.Runtime.CompilerServices
Imports System.ComponentModel

Public Module QRCodeExtension

    ''' <summary>
    ''' 透過 Google 提供的 chart 服務,產生對應的 img QRCode標籤。
    ''' </summary>
    ''' <param name="helper">擴充方法</param>
    ''' <param name="data">要產生QRCode的字串</param>
    ''' <param name="alt">QRCode說明文字</param>
    ''' <param name="size">大小(px),預設 80px*80px</param>
    ''' <param name="margin">留白(px),預設 4px</param>
    ''' <param name="errorCorrectionLevel">回復等級,預設 L</param>
    ''' <param name="htmlAttributes">其他html屬性,預設 Nothing</param>
    ''' <returns>回傳 img 標籤</returns>
    <Extension()>
    Public Function QRCode(helper As HtmlHelper,
                           data As String,
                           alt As String,
                           Optional size As Integer = 80,
                           Optional margin As Integer = 4,
                           Optional errorCorrectionLevel As QRCodeErrorCorrectionLevel = QRCodeErrorCorrectionLevel.Low,
                           Optional htmlAttributes As Object = Nothing) As MvcHtmlString

        ' 錯誤處理
        If String.IsNullOrEmpty(data) Then
            Throw New ArgumentNullException("data", "不得空白.")
        End If

        If size < 1 Then
            Throw New ArgumentOutOfRangeException("size", size, "必須大於零.")
        End If

        If margin < 0 Then
            Throw New ArgumentOutOfRangeException("margin", margin, "必須大於或等於零.")
        End If

        If Not [Enum].IsDefined(GetType(QRCodeErrorCorrectionLevel), errorCorrectionLevel) Then
            ' 需 Imports System.ComponentModel 才會有 InvalidEnumArgumentException
            Throw New InvalidEnumArgumentException("errorCorrectionLevel",
                                                   CType(errorCorrectionLevel, Integer),
                                                   GetType(QRCodeErrorCorrectionLevel)
                                                   )
        End If

        ' 使用 google 的 chart 服務
        ' 參數參考:http://code.google.com/intl/zh-TW/apis/chart/infographics/docs/qr_codes.html
        Dim url As String = String.Format(
                                "http://chart.apis.google.com/chart?cht=qr&chld={2}|{3}&chs={0}x{0}&chl={1}",
                                size,
                                HttpUtility.UrlEncode(data),
                                errorCorrectionLevel.ToString()(0),
                                margin)

        ' 產生img Tag
        Dim tag As New TagBuilder("img")

        tag.MergeAttribute("src", url)
        tag.MergeAttribute("alt", alt)
        tag.MergeAttribute("title", alt)

        ' width, height 兩個屬性,可加可不加,不影響產出 QRCode 結果。
        tag.MergeAttribute("width", size.ToString())
        tag.MergeAttribute("height", size.ToString())

        If htmlAttributes IsNot Nothing Then
            tag.MergeAttributes(New RouteValueDictionary(htmlAttributes))
        End If

        Return New MvcHtmlString(tag.ToString(TagRenderMode.SelfClosing))
    End Function

End Module

''' <summary>
''' 回復等級列舉。
''' 參考:http://code.google.com/intl/zh-TW/apis/chart/infographics/docs/qr_codes.html 說明文件。
''' </summary>
Public Enum QRCodeErrorCorrectionLevel
    ''' <summary>
    ''' 從 7% 的錯誤資料中回復.
    ''' </summary>
    Low
    ''' <summary>
    ''' 從 15% 的錯誤資料中回復.
    ''' </summary>
    Medium
    ''' <summary>
    ''' 從 25% 的錯誤資料中回復.
    ''' </summary>
    QuiteGood
    ''' <summary>
    ''' 從 30% 的錯誤資料中回復.
    ''' </summary>
    High
End Enum

在要使用的 View 之中引用擴充方法 Namespace,即可使用。我們以 Home/Index 為例:

<%@ Page Language="VB" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
<%@ Import Namespace="Mvc3QRCode.QRCodeExtension" %>
<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent" runat="server">
    首頁
</asp:Content>

<asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
    <h2><%: ViewData("Message") %></h2>
    <p>
    預設 80*80, margin = 4<br />
    <%: Html.QRCode("http://kkbruce.blogspot.com", "KKBruce部落格網址") %>
    </p>
    <p>
    修改為 150*150, margin = 1<br />
    <%: Html.QRCode("http://kkbruce.blogspot.com", "KKBruce部落格網址", size := 150, margin := 1) %>
    </p>
</asp:Content>

建置,Ctrl+F5,執行網頁。

Visual Basic - QRCode Extension 執行圖
圖一:Visual Basic - QRCode Extension 執行圖
有前人種樹真好。^_^

Visual Basic - 如何強化程式邏輯能力?

如果你有幸進入程式設計這一行,那就不免要動腦。
沒事多動腦,多動腦沒事。
動腦,其實就是在規劃撰寫處理整理程式邏輯,那平常要如何加強這方面的能力呢?
就我本身而言,我覺得有以下四點:

Tool - 免費Visual Studio, SQL Server, Visual Basic, Visual C#, Visual C++, Windows Phone等開發工具下載點總整理(2011/11月版)

常有人問,Visual Basic 那裡下載?Viusal Studio 那裡下載?SQL Server 那裡下載?在這裡做個總整理,讓需要下載微軟開發工具的人,從這裡就可以找到全部的下載點,而不用在去 一一Search。

Visual Studio 2010 Express 單一語言開發工具下載

如果你要下載 Visual Studio 2010 / Visual Studio 2011 相關免費版本開發工具( Express ),請到這裡下載 VISUAL STUDIO 2010 EXPRESS PRODUCTS

Visual Studio 2010 Express
圖一:Visual Studio 2010 Express (from microsoft)
以上你在能在畫面上找到一個「Select language...」的下載選單,選擇「中文」即可下載中文安裝檔。以上合適單一語言學習者下載安裝使用。

Visual Studio 2010 Express 繁體中文下載



不過,我個人是比較推薦使用整合式開發環境,即 Visual Studio 2010 Express ISO Images,你能下載到:
整合式的意思是,它包含了基本上所有核心裡最重要開發工具,例如,Visual Basic、Visual C#、Visual Web Developer,但如果是 Windows Phone、ASP.NET MVC … 還必須加裝外掛程式來加上去。另外一點是,安裝程式大小,會比單一語言開發工具大的多。

下載 ISO 之後,建議使用老字號虛擬光碟Daemon Tools Lite來掛載安裝即可。
Microsoft Visual Studio 2010 Service Pack 1 必須另外下載安裝。

Visual Studio 2008 Express 繁體中文下載


如果你有特別需求,需要使用到 Visual Studio 2008 Express 版本來開發,那請到 VISUAL STUDIO 2008 EXPRESS EDITIONS,來下你所需的安裝檔,目前 Microsoft 已經很好心的幫你整合為 SP1 版本。

Visual Studio 2008 繁體中文版 ISO 檔

Microsoft SQL Server 2008 R2 Express Edition 繁體中文下載


除了開發工具,另一個重點當然是資料庫,如果我們想下載免費版的 SQL Server Express,那就要到:

Database Only 的意思是,只有資料庫沒有管理工具 SSMS,如果你習慣透過 Visual Studio 設計、管理資料庫,那有無 SSMS 都可,如果是要含管理工具:
以上這兩個是有含 SSMS 管理工具。

Microsoft SQL Server Code Name "Denali" 繁體中文下載

SQL Server Code Name 'Denali'
圖二:SQL Server Code Name "Denali" (from microsoft)
如果你想玩 SQL Server 目前最新測試版 Denali,你可以到 Download SQL Server Code Name "Denali" Express CTP3 (有繁體中文版可下載) 或 SQL Server Code Name "Denali" CTP3 (有繁體中文版可下載),與 Microsoft SQL Server Express Edition 一樣,有分為單純資料庫和資料庫加 SSMS 管理工具版本。

補充:這裡有一份 SQL Server 不錯的功能比較表,可以參考看看 Compare Microsoft SQL Server Editions

Visual Studio LightSwitch 下載

Visual Studio LightSwitch
圖三:Visual Studio LightSwitch  (from microsoft)

Visual Studio 瘦身版,細部比較可參考 Compare to Visual Studio Pro,即保留更核心的能功。
Download the Visual Studio LightSwitch trial,目前是 90天試用版。

Visual Studio 11 開發者預覽與開發工具下載

Visual Studio 無疑是非常棒的開發工具,目前最新開發者預覽版為 Visual Studio 11,你可以在 Microsoft® Visual Studio® 11 Developer Preview (Web Installer) 下載,如果你本機已經有安裝 Visual Studio 2008 / Visual Studio 2010 也沒關係,都可以再安裝這個 Visual Studio 11 開發者預覽版。另,以上的 Visual Studio 11 已包含 SQL Server Code Name "Denali" CTP3 未含 SSMS,是可以測試 Denali 的相關新功能的。


2011/10/20 補充:Microsoft Dreamspark

這是MS MVP 鄭子璉提供的網站:Microsoft Dreamspark,真的是超棒的,應用盡有,是由微軟原廠整理提供,當然,整理出來的東西更完整及全面。我玩了一下,它比較是提供英文版本為主。Express 版有提供繁體中文版。另一個問題是,一定要有 msn、hotmail 之類帳號才能下載。

Visual Basic - Login/登入/登錄之物件導向實作

Login VB-OOP 實作前言

Login (登入/登錄) 動作,不管在任何應用系統 (AP, Application System)都能看到它的影子。像我的手機,一開機先問個 PIN 密碼...光一個手機,就能有好多密碼。
  1. 進公司刷卡(Card);
  2. 提錢(Card);
  3. 上網(Accout/Password);
  4. 回家開門(Key);
  5. 開車(Key);
  6. 指紋(生物辨識);

你能發現每天從早到晚,其實都在不斷的 Login / Logout,出門上班,對「家」而言,你是 Logout,開車上班,對「車」而言,你是 Login,到達公司,對「車」而言,你是 Logout,進公司,必須刷卡才能進入,那是 Login,下班刷卡離開公司,那是 Logout。

指紋辨識,這幾年拜 NB 幫的忙,很多 NB 都把指紋辨識當成基本配備,省下了打 Keyboard 的時間,刷一下手指,超方便的。還記得,大學時在「國家太空中心」打工,國家太空中心可以使用「掌紋辨識」,在當時可以說是很先進方便,辨識率也不錯,就我在那裡打工時間裡,最少沒有出錯過。

以下是以程杰(大話設計模式作者) Blog 裡兩篇文章為基礎,以這兩篇文章實作出 Visual Basic 可執行版本。

  1. 凭什么要用面向对象编程——面向对象重要设计原则概述
  2. 凭什么要用面向对象编程(补充)
非常推薦給學習過物件導向的人來看,Login 範例非常實用,只要是「AP」你一定會碰到 Login 問題,學習如何透過物件導向來處理此一問題。

我覺得另一重點是「思緒」,想想,如果是你,你會怎麼處理?是否會有一樣的思緒?而且我認為文章中提出的問題,都還算常見,不算是那種為了寫書或寫文章而特別設計的例子。

範例一:網頁登入 (CodeBehind) (對應文章 2007-7-1  對話)



WebForm_1 專案 - Login.aspx

Login.aspx 介面 HTML。

<h2>網頁登錄 ( CodeBehind )</h2>
<form id="form1" runat="server">
<div>
    <asp:Label ID="lbName" runat="server" Text="帳號:"></asp:Label>
    <asp:TextBox ID="tbName" runat="server"></asp:TextBox>
    <br />
    <asp:Label ID="lbPassword" runat="server" Text="密碼:"></asp:Label>
    <asp:TextBox ID="tbPassword" runat="server" TextMode="Password"></asp:TextBox>
    <br />
    <br />
    <asp:Button ID="btnLogin" runat="server" Text="登錄" />
</div>
</form>

Login.aspx.vb CodeBehind 程式碼。

Public Class Login
    Inherits System.Web.UI.Page

    Protected Sub tbLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click
        Dim userName As String = tbName.Text
        Dim passWord As String = tbPassword.Text

        If (login(userName, passWord)) Then
            Response.Redirect("LoginOK.aspx")
        Else
            Response.Redirect("LoginFail.aspx")
        End If
    End Sub

    Private Function login(userName As String, password As String) As Boolean
        ' 訪問資料庫,進行帳號及密碼比對
        ' --- 此處只是為了 Demo 方便,所以寫死 ---
        If userName = "kkbruce" And password = "12345" Then
            Return True
        Else
            Return False
        End If
    End Function

End Class

此範例為最最最基礎的 Login 程式碼,也是最最最一般的 Login 程式碼。注意,為了 Focus 在「Login」這件事情上面,我們的範例不會出現任何資料庫相關程式碼,要注意的地方,我都有打上註解。

另外準備兩個網頁, LoginOK.aspx及LoginFail.aspx,內容很簡易,一個打上「網頁登錄成功!」,一個打上「網頁登錄失敗!」,執行我們的第一個專案,這已經是一個能動的範例了。

範例二:手機登錄 ( Class UserAdmin ) (對應文章 2007-7-25 對話)


我個開一個新 WebForm 專案,假設它是手機登錄介面。

WebForm_2 專案 - Login.aspx


PhoneLogin.aspx 介面 HTML 同 WebForm_1 專案。

PhoneLogin.aspx.vb

Public Class Login
    Inherits System.Web.UI.Page

    Protected Sub tbLogin_Click(sender As Object, e As EventArgs) Handles btnLogin.Click
        Dim userName As String = tbName.Text
        Dim password As String = tbPassword.Text

        ' 使用 Class UserAdmin 提供的認證功能。
        Dim ua As New UserAdmin
        If (ua.Login(userName, password)) Then
            Response.Redirect("LoginOK.aspx")
        Else
            Response.Redirect("LoginFail.aspx")
        End If
    End Sub
End Class

''' <summary>
''' 用戶管理類別
''' </summary>
Public Class UserAdmin

    ''' <summary>
    ''' 登錄資料確認函式
    ''' </summary>
    ''' <param name="userName">帳號</param>
    ''' <param name="password">密碼</param>
    ''' <returns>Boolean</returns>
    Public Function Login(userName As String, password As String) As Boolean
        ' 訪問資料庫,進行帳號及密碼比對
        ' --- 此處只是為了 Demo 方便,所以寫死 ---
        If userName = "kkbruce" And password = "12345" Then
            Return True
        Else
            Return False
        End If
    End Function
End Class

我們把原本的 Login 函式提升至一個 UserAdmin 類別裡,這樣就可以給大家共用。


Visual Basic - Reflection, 反映教學筆記(10) Object-oriented 物件導向範例程式

Class 圖表

我們先看來整個 Class 圖表

圖一:Assembly Object-oriented Class (一) (點擊看大圖)
重點在 SampleFactory,SampleFactory是類別產生工廠。

圖二:Assembly 相關類別 (點擊看大圖)
我們透過一個抽象類別 SuperAssembly 來整理所有的初始化及屬性工作,直接繼承使用。

圖三:Attribute 相關類別(點擊看大圖)
同上,透過抽象類別 SuperAttribute 來整理所有初始化及屬性工作,直接繼承使用。

Visual Basic - Reflection, 反映教學筆記(9) 執行環境中產生程式碼

總和前面所有,在這一篇裡,我們將整合前面所有內容,從無到有完完全全自行建立自有組件、自有模組、自有型別、自有成員、自有方法、自有欄位、自有屬性,最後還能將動態建立的組件儲存成實體組件檔案 ( *.dll )。

一樣,應該要注意的內容,我都註解到程式碼中,與前一篇「Visual Basic - Reflection, 反映教學筆記(8) 執行動態程式碼」很像,就是 Step by Step 的建立出你的組件。重點在於我們是使用 System.Reflection.Emit 類別*Builder 結尾的類別,來動態建立出我們的組件。

*Builder 結尾的類別類似觀念,我們前面「」就已經談過,觀念一樣,我就不在重覆。你要建立什麼就是 ...Builder 類別。記的要先Imports System.Reflection.Emit。

RunTimeCreateAssembly 副程式

#Region "執行環境中產生程式碼"
    ''' <summary>
    ''' 建立動態組件
    ''' </summary>
    Private Sub RunTimeCreateAssembly()
        ' 1. 建立組件
        Console.WriteLine("定義組件")
        Dim tempName As New AssemblyName()
        tempName.Name = "KKBruceSampleAssembly"
        tempName.Version = New Version("1.0.0.0")

        ' 使用 DefineDynamicAssembly 動態產生組件
        Dim AssemBL As AssemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(tempName, AssemblyBuilderAccess.RunAndSave)

        ' 2. 建立模組
        Console.WriteLine("定義模組")
        ' DefineDynamicModule 動態產生模組
        ' 如果要建立一個單一檔案的組件,並將此組件序列化在磁碟之中,最後的Save名稱必須與以下DefineDynamicModule裡名稱相同。
        Dim ModuleBL As ModuleBuilder = AssemBL.DefineDynamicModule(tempName.Name, tempName.Name & ".dll")

        ' 3. 建立型別
        Console.WriteLine("定義型別")
        ' DefineType 動態產生型別
        Dim BruceType As TypeBuilder = ModuleBL.DefineType("KKBruceType", TypeAttributes.Class Or TypeAttributes.Public)
        ' 指定父類別及介面給DefineType
        'Dim tb As TypeBuilder = mb.DefineType("KKBruceMyNewType", TypeAttributes.Class Or TypeAttributes.Public,
        '                                      GetType(Hashtable),
        '                                      New Type() {GetType(IDisposable)})


        ' 4. 建立成員
        Console.WriteLine("定義相關成員")
        ' 4.1 建立建構子
        Console.WriteLine("     定義建構子")
        ' MethodAttributes代表建構子的類型
        'Dim ConstructorBL As ConstructorBuilder = BruceType.DefineDefaultConstructor(MethodAttributes.Public)

        ' 4.2 利用ConstructorBuilder建立ILGenerator物件

        ' --- 註:執行會產生錯誤 ---
        ' --- 註:依MSDN查詢:Runtime 會為預設建構函式產生程式碼。因此,如果嘗試取得 ILGenerator,則會擲回例外狀況。 ---

        'Console.WriteLine("     建立ILGenerator物件")

        ' ILGenerator 可產生 Microsoft Intermediate Language (MSIL) 指令。
        'Dim codeGen As ILGenerator = ConstructorBL.GetILGenerator()

        ' Emit:多載。放置指令到 Just-In-Time (JIT) 編譯器的 Microsoft Intermediate Language (MSIL) 資料流中。
        ' OpCodes 類別:提供 Microsoft Intermediate Language (MSIL) 指令的欄位表示,以用於 ILGenerator 類別成員 (例如 Emit) 的發出。
        ' Ret欄位:從目前方法傳回,將被呼叫端評估堆疊的傳回值 (如果有的話) 推入至呼叫端的評估堆疊。 
        'codeGen.Emit(OpCodes.Ret)

        ' 4.3 建立方法
        Console.WriteLine("     定義方法")
        Dim MethodBLOne As MethodBuilder = BruceType.DefineMethod("AddStringOne", MethodAttributes.Public, Nothing, New Type() {GetType(String)})
        ' 第三個參數:建新新方法的回傳型別
        ' 第四個參數:參數型別
        Dim MethodBLTwo As MethodBuilder = BruceType.DefineMethod("AddStringTwo", MethodAttributes.Public Or MethodAttributes.Static,
                                                     Nothing,
                                                     New Type() {GetType(String)})

        ' 4.4 建立私有欄位
        Console.WriteLine("     定義私有欄位")
        Dim FieldBL As FieldBuilder = BruceType.DefineField("_count", GetType(Int32), FieldAttributes.Private)

        ' 4.5 建立屬性
        Console.WriteLine("     定義屬性")
        ' PropertyAttributes.None 列舉常數不能定義所有的屬性
        Dim PropertyBL As PropertyBuilder = BruceType.DefineProperty("Count", PropertyAttributes.None, GetType(Int32), Type.EmptyTypes)

        ' 4.6 在方法中定義設得或設定屬性值
        Console.WriteLine("     定義方法中設得或設定屬性值")
        ' 注意,使用MethodAttributes
        Dim getMethodAttributes As MethodAttributes = MethodAttributes.Public Or
                                                MethodAttributes.SpecialName Or
                                                MethodAttributes.HideBySig

        Dim propGet As MethodBuilder = BruceType.DefineMethod("get_Count", getMethodAttributes, GetType(Int32), Type.EmptyTypes)
        ' 讓方法可以設定與取得屬性值
        PropertyBL.SetGetMethod(propGet)

        ' 5. 建立型別
        Console.WriteLine("產生新型別")
        ' --- 註:建立型別之前要為所有 MethodBuilder 執行 GetILGenerator() ---
        ' --- 註:不然會產生一個「方法 'xxx' 沒有方法主體。」的錯誤,這裡 Debug 花了我好久!XD ---
        ' --- 註:參考:http://msdn.microsoft.com/zh-tw/library/system.reflection.emit.methodbuilder.definegenericparameters%28VS.85%29.aspx 範例才找出解法 ---
        Console.WriteLine(" 產生所有方法的ILGenerator")
        RunMethodBLGetILGenerator(MethodBLOne)
        RunMethodBLGetILGenerator(MethodBLTwo)
        RunMethodBLGetILGenerator(propGet)

        Console.Write(" 產生型別")
        Dim KKBruceType As Type = BruceType.CreateType()

        ' 5.1 顯示此型別方法
        Console.WriteLine()
        Console.WriteLine("     顯示型別方法(注意我們產生動態產生的方法)")
        GetRunTimeAssemblyMethods(KKBruceType)
        Console.WriteLine()
        Console.ReadLine()

        ' 6. 儲存組件到磁碟中
        Console.WriteLine("儲存組件到磁碟中...")
        ' 一但被寫入到磁碟中,任何程式都可以載入這個組件使用。
        AssemBL.Save(tempName.Name & ".dll")
        Console.WriteLine("儲存完成。(請參考專案目錄下「\bin\Debug」產生的動態組件 " & tempName.Name & ".dll")
        Console.ReadLine()
    End Sub

    ''' <summary>
    ''' 傳回這個方法的 ILGenerator
    ''' </summary>
    ''' <param name="methodInstance">MethodBuilder 的執行個體</param>
    ''' <remarks></remarks>
    Private Sub RunMethodBLGetILGenerator(methodInstance As MethodBuilder)
        ' MethodBuilder.GetILGenerator()
        ' 傳回這個方法的 ILGenerator,使用預設 Microsoft Intermediate Language (MSIL) 資料流的 64 位元大小。 
        Dim ilgen As ILGenerator = methodInstance.GetILGenerator()
        ilgen.Emit(OpCodes.Ldnull)
        ilgen.Emit(OpCodes.Ret)
    End Sub

    ''' <summary>
    ''' 取得動態組件方法屬性
    ''' </summary>
    ''' <param name="KKBruceType">型別</param>
    Private Sub GetRunTimeAssemblyMethods(ByVal KKBruceType As Type)
        Console.WriteLine("     Full Name: {0}", KKBruceType.FullName)
        For Each m As MemberInfo In KKBruceType.GetMethods()
            Console.WriteLine("         Member({0}):{1}", m.MemberType, m.Name)
        Next
    End Sub
#End Region

執行結果

執行環境中產生程式碼
定義組件
定義模組
定義型別
定義相關成員
     定義建構子
     定義方法
     定義私有欄位
     定義屬性
     定義方法中設得或設定屬性值
產生新型別
 產生所有方法的ILGenerator
 產生型別
     顯示型別方法(注意我們產生動態產生的方法)
     Full Name: KKBruceType
         Member(Method):AddStringOne
         Member(Method):AddStringTwo
         Member(Method):get_Count
         Member(Method):ToString
         Member(Method):Equals
         Member(Method):GetHashCode
         Member(Method):GetType


儲存組件到磁碟中...
儲存完成。(請參考專案目錄下「\bin\Debug」產生的動態組件 KKBruceSampleAssembly.dll

注意我們自行新增的 Method (AddStringOne, AddStringTwo, get_Count),確認一下產生的 DLL 檔:

圖一:儲存動態程式碼為組件

參考資料


下載 Reflection (1) ~ (9) 範例程式碼

感謝你們看到這九篇文章,我將第一篇至第九篇的範例整理成一個小小小小的 Console 選擇遊戲,以下是原始碼專案,必須使用 Visual Studio 2010 / .NET Framework 4 才能重新編譯。已編譯執行檔在 \bin\Debug\AssemblyDemo.exe,如果你想試試我前面介紹的 Reflection 工具,也可以從已編譯執行檔裡去看原始碼。



解壓縮密碼:KKBruce

此範例程式碼是「太濕版」一點也不「DRY, 乾」,全部用副程式在呼叫,下一篇我會將把此範例改寫為 OO (物件導向, Object-oriented) 的範例,算是大重構,不過從其中我們也能看到物件導向的優點,很多事你硬要使用「副程式/函式」來做是會…濕透了。

Visual Basic - Reflection, 反映教學筆記(8) 執行動態程式碼

在 Reflection (反映)中,支援不需要提前參考就可以從組件中動態地建立物件。我們可以動態去載入一個組件,不必事先 Import ( C# 中 using ),而且在組件中執行程式碼。

這一篇的內容比較複雜,要注意的地方我都寫成註解,所以我們直接來看相關副程式。我們先來看一個最簡單,也是一般在使用組件的方式。

UseHashtable 副程式

''' <summary>
''' 使用一般 Hashtable 方法來顯示
''' </summary>
Private Sub UseHashtable()
    Console.WriteLine("產生Hashtable")
    Dim tb As New Hashtable()
    tb.Add("Hi", "Hello")
    Console.WriteLine("Hash Count: {0}", tb.Count)
    Console.WriteLine()
    Console.ReadLine()
End Sub

UseHashtable 副程式執行結果

一般方式,使用Hashtable
產生Hashtable
Hash Count: 1

以上是很一般使用 Hashtable 的方式,沒有任何特別之處。但如果我們是要透過動態程式碼的方式來撰寫,就會複雜很多。

Visual Basic - Reflection, 反映教學筆記(7) - BindingFlags 過濾 Member(成員)

我們再談談Type 的 Member,我們前面寫過一段簡單取得 Member 的程式碼:

''' <summary>
''' 簡單測試及顯示型別 GetMembers() 方法
''' </summary>
''' <param name="type">型別 (即Type) 物件</param>
Private Sub ShowGetMembers(type As Type)
    Console.WriteLine("取得型別Members")
    For Each mi As MemberInfo In type.GetMembers()
        Console.WriteLine("    Member的 Type 及 名稱")
        Console.WriteLine("          {0}:{1}", mi.MemberType, mi.Name)
        ShowMemberType(mi)
    Next
    Console.WriteLine()
    Console.ReadLine()
End Sub

基本上,這沒什麼問題。但如果我想的資訊只需要「特定成員呢?」這時候我們就必須設定一個 BindingFlags 列舉型別給 type.GetMembers(),BindingFlags 指定控制繫結的旗標和由反映為引導的成員和型別搜尋方式。

這些 BindingFlags 會控制在叫用 (Invoke)、建立、取得、設定和尋找成員和型別的 System、System.Reflection 和 System.Runtime 命名空間中眾多類別的繫結。 

BindingFlags 用於下列 Type 方法和其他地方,例如,

  • MethodBase.Invoke
  • GetMembers 
  • GetEvents 
  • InvokeMember 
  • Activator.CreateInstance 
  • GetConstructor 
  • GetConstructors 
  • GetMethod 
  • GetMethods 
  • GetField 
  • GetFields 
  • GetEvent 
  • GetProperty 
  • GetProperties 
  • GetMember 
  • FindMembers 

其中 InvokeMember 和 GetMethod 特別重要。

Visual Basic - Reflection, 反映教學筆記(6) 暸解MethodBody

前面幾篇,我們可以透過型別、型別成員…等去瞭解型別的結果,但還是無法瞭解型別包含的程式碼,這時就可以透過 MethodBody 來瞭解。MethodBody 是一個特殊的物件,裝載著區域變數及實際中介語言( Intermediate Language;IL )。

在開始以下範例前,我們先進行一個準備工作。我們先從 MSDN 複製一個範例 Class 到我們的主程式碼的最下方。

#Region "MSDN中 MethodBase.GetMethodBody 方法範例"
''' <summary>
''' From MSDN: MethodBase.GetMethodBody 方法範例
''' http://msdn.microsoft.com/zh-tw/library/system.reflection.methodbody.aspx
''' </summary>
Public Class Example
    Public Shared Sub Main()
        ' 請從 MSDN 複製
    End Sub
End Class
#End Region

因為等一會的程式,必須從一個 Class 取得型別,從型別裡取得 MethodInfo ,再從 MethodInfo 取得 MethodBody,最後就能從 MethodBody 得到我們想要的資訊。