2012年3月13日 星期二

.Net Framework 4.5與Visual Studio 11 –工具與程式語言新功能

 

原文刊於 .NET Magazine國際中文電子雜誌

此篇文章改寫原刊於 .NET Magazine的文章,原文是使用Visual Studio 11 Developer Preview版工具撰寫的,本文改用Visual Studio Beta工具測試,重新抓圖並修訂部分工具的差異。原文URL:

http://blogs.uuu.com.tw/Articles/post/2011/11/02/Net-Framework-45%E8%88%87Visual-Studio-11-%E2%80%93%E5%B7%A5%E5%85%B7%E8%88%87%E7%A8%8B%E5%BC%8F%E8%AA%9E%E8%A8%80%E6%96%B0%E5%8A%9F%E8%83%BD.aspx

本文撰寫時使用的工具是Visual Studio 11 Beta,資料庫則為SQL Server 2012 Express版,因此本文探討的內容在正式版上市時可能不適用。

 

Visual Studio 11 Beta開發工具

Visual Studio 2010與Visual Studio 11 Beta開發工具兩者可以並存在相同的電腦環境上。更好的是,專案檔案與方案檔案具備向下相容的能力。不過,參考圖1所示,目前在筆者的電腦中安裝了.NET Framework 4版,從Visual Studio 11 Beta專案的「Target Framework」設定來看,目前Visual Studio 11 Beta不僅能夠開發預設的.NET Framework 4.5版程式,也能夠開發前版.NET Framework2~.NET Framework 4的應用程式。

clip_image002

圖 1:Visual Studio 11 Beta專案的「Target Framework」設定。

 

 

 

當你在Visual Studio 11 Beta開啟使用Visual Studio 2010設計的方案或專案時,會有提示訊息跳出來,例如底下開啟一個使用Visual Studio 2010撰寫,「Target Framework」為.NET Framework 3.5的專案會出現以下訊息:

clip_image004

圖 2:升級提示訊息。

你可以手動將應用程式升級到4.5版,只要設定專案屬性頁的Target Framework為適當版本即可:

clip_image006

圖 3:設定Target Framework。

此時會顯示提示訊息:

clip_image008

圖 4:升級提示訊息。

當然,被Visual Studio 11 Beta開啟並升級成.NET Framework 4.5的專案,後續無法再利用Visual Studio 2010開啟繼續進行設計的動作。但若是在Visual Studio 11 Beta中將專案的目標Framework設定為.NET Framework 4,則使用Visual Studio 2010還是可以開啟此專案。

 

「Solution Explorer」視窗

「Solution Explorer」視窗可以用來檢視類別的成員、進行成員搜尋動作,或者開啟兩個以上的「Solution Explorer」視窗,便於在多個螢幕中顯示。

在「Solution Explorer」視窗中,你可以檢視專案中某項目包含的內容,例如,參考圖5所示,MainWindow.xaml.cs檔案中,MainWindow類別包含了MainWindow方法以及其它成員,您也可以檢視其基礎型別(Base Types)、衍生型別(Derived Types),或被哪些項目使用(Is Used By)。

clip_image010

圖 5:檢視項目包含的內容。

你也可以使用Contains功能來檢視類別中包含哪些成員,例如在MainWindow的父類別Window項目上,按滑鼠右鍵,選取「Contains」:

clip_image012

圖 6:Contains。

將列出Window類別所有成員:

clip_image014

圖 7:列出Window類別所有成員。

「Solution Explorer」視窗上方的向左、向右鍵頭可以回到上一個、或下一個檢視畫面。

此外「Solution Explorer」視窗也提供了簡易的搜尋功能,便於找尋專案中的程式碼,底下的圖8顯示輸入「Initial」關鍵字之後,表列出符合查詢條件的結果。

clip_image016

圖 8:「Solution Explorer」視窗的搜尋功能。

另外您也可以利用「Solution Explorer」視窗來檢視方法的呼叫歷程,類似前版Visual Studio 2010 C#專案中的「Call Hierarchy」視窗的功能,參考圖9所示。

clip_image018

圖 9:檢視方法的呼叫歷程。

當設計工具中開啟的檔案很多時,有時很難尋找,你可以利用將常用檔案釘選(Pin)起來,以便於找尋,參考圖10所示:

clip_image020

圖 10:檔案釘選功能。

 

檔案預覽功能(File preview)

Visual Studio 11 Beta新增一個檔案預覽(File preview)功能,可以將未開啟在設計畫面中的檔案自動開啟提供預覽的動作。預設在Visual Studio 11 Beta 的「Tools」->「Options」->「Environment」->「Tabs and Windows」頁面中,已勾選「Allow new files to be opened in the preview tab」項目,和「Single – click opens files in the preview tab」-「Solution Explorer ( Alt + Click to avoid previewing)」以及「Find Results」項目,參考圖11所示。

clip_image022

圖 11:啟用檔案預覽(File preview)功能。

舉例來說,參考圖12,當應用程式執行,進入中斷模式來除錯時,您可以直接點選程式碼視窗中的Employee類別上,按下滑鼠右鍵,從突顯示選單,選取「Go To Definition」項目,Visual Studio 11 Beta會自動地開啟Employee類別定義所在的檔案,表列在編輯視窗的右上方。

clip_image024

圖 12:檔案預覽。

你可以點選預覽視窗上的「Open」按鈕,它就會變成是一般的分頁,參考圖13所示。

clip_image026

圖 13:檔案預覽Open功能。

若是使用Visual Studio 11 Beta的功能搜尋程式碼,搜尋的結果會表列在「Find Results」視窗。現在「Find Results」視窗也提供檔案預覽的功能。例如底下範例搜尋應用程式中的Employee類別,當你點選「Find Results」視窗搜尋到的第一個項目時,預覽檔案的分頁也會自動顯示,參考圖14所示。

clip_image028

圖 14:「Find Results」視窗檔案預覽的功能。

 

「Quick Launch」視窗

當Visual Studio開發工具的功能愈來愈複雜時,想要尋找選單中的選項就是一件相當困難的事。還好,在Visual Studio 11 Beta工具的右上角有一個新的「Quick Launch」視窗,可以快速地找到Visual Studio 11 Beta的選單選項,參考圖15所示,你可以按下「CTRL+Q」來操作它。

clip_image030

圖 15:「Quick Launch」視窗。

例如想要變更程式碼的字型,可以在「Quick Launch」視窗輸入「fonts」字串,按下鍵盤的「Enter」鍵,Visual Studio 11 Beta便會自動表列出和Fonts相關的設定步驟,顯示在下方。當你點選列在下方的「Environment -> Fonts and Colors」選項時,Visual Studio 11 Beta也會自動顯示「Options」對話盒,並停留在「Fonts and Colors」設定畫面,您便可以馬上更改相關的設定,非常的便利,參考圖16所示。

clip_image032

圖 16:「Quick Launch」視窗。

「Quick Launch」視窗也可以應用在搜尋已開啟的文件,例如圖17顯示,若想要快速地切換到已開啟的MainWindow.xaml檔案,只要輸入關鍵字,如「MainWi」就行了:

clip_image034

圖 17:在「Quick Launch」視窗搜尋已開啟的文件。

 

增強的搜尋功能

隨著.NET Framework版本的更新,可以使用在應用程式介面的控制項以及專案檔案也愈來愈多,不方便找尋。現在「Toolbox」、「Solution Explorer」與「Error List」…等多個視窗都具備搜尋的功能,方便尋找控制項、檔案或錯誤資訊等等。

要使用「Toolbox」搜尋的功能,您可以利用上方的「Search Toolbox」文字方塊輸入關鍵字來篩選控制項,或是直接將游標停留在工具箱上後,直接輸入關鍵字。例如以下輸入「ca」字串,Visual Studio 11 Beta工具箱的游標會自動停留在Calendar控制項上,按下鍵盤的Tab鍵可以切換到下一個符合搜尋關鍵字的控制項上,若要取消查詢動作,可以按下鍵盤的ESC鍵,參考圖18所示。

clip_image036

圖 18:搜尋控制項。

 

使用規則運算式搜尋

「Quick Find」、「Quick Replace」、「Find in Files」與「Replace in Files」視窗可以用來搜尋、取代專案或檔案中的內容。現在搜尋時又新增一個選項,可以使用規則運算式定義篩選條件來進行搜尋。

參考底下的「Quick Find」視窗,您可以按下「CTRL + F」來開啟這個視窗,它會出現在程式編輯視窗的右上角。搜尋時只要輸入有意義的規則運算式,並勾選「Use Regular Expression」選項即可,搜尋到的內容會以黃色標示。若要檢視下一個符合篩選條件的內容,可以按下「Quick Find」視窗右上方的向右、向左箭頭,或按下鍵盤F3 (Find Next) 或SHIFT + F3 (Find Previous) ,參考圖19所示:

clip_image038

圖 19:使用規則運算式搜尋。

 

Visual Basic與C#新功能-Async

Visual Basic與C#新增一個非同步功能(Async),讓非同步應用程式的開發能夠更簡單。

Visual Basic與C#都新增兩個運算子,Async (適用於Visual Basic),async(適用於C#)以及 Await (適用於Visual Basic),await(適用於C#)。 在定義方法時,方法的前方可以加上Async(async)關鍵字,以通知編譯程式某個方法或某一個Lambda運算式是非同步的,這個標識async關鍵字的方法稱為-非同步方法(async method)

非同步方法(async method)宣告時,要加上async關鍵字。

await運算子則是套用在一項非同步方法的作業(Task)上,停滯此方法程式的執行,直到作業執行完畢為止,才將執行權交還給呼叫的方法。作業(Task)就是你欲以非同步方式執行的動作。不過,await運算子只能夠在有標示async關鍵字的非同步方法中使用。

底下的程式碼片斷中的AsyncMethod方法就是一個非同步方法,方法中await關鍵字套用在叫用Task.Delay方法這一行程式,讓程式暫停一秒執行,然後再回傳「Hello」字串:

C#語法:

public static async Task< string > AsyncMethod()     
{
    await Task.Delay( 1000 );
    return "Hello" ;
}

Visual Basic語法:

Public Async Function AsyncMethod( ) As Task( Of String )
    Await Task.Delay( 1000 )
    Return "Hello"
End Function

await運算式並不會停滯目前正在執行中的執行緒,而是告知編譯器將非同步方法剩餘未執行的程式碼標示為一項await 工作,控制權會交還呼叫非同步方法的呼叫者。當非同步作業執行完畢時,就會執行非同步方法剩餘未執行的程式碼。

再者,以C#程式語言來說,非同步方法的回傳值只能夠是void、Task或Task<T>型別,T代表回傳值的型別。若以Visual Basic程式語言來說,非同步方法可以是一個sub,或function,但function的回傳值只能夠是Task或Task(of T)型別。此例中AsyncMethod非同步方法回傳的是一個Task<string>(C#),Task(Of String) (Visual Basic),這個方法回傳值的型別是string型別。在命名時按慣例,會以「Async」字串結尾。

C#語法:

private static async void GetNameAsync()
        {
            Task< string > myTask = (Task< string >) AsyncMethod();
            string result = await myTask;
            Console.WriteLine( result );
        }

Visual Basic語法:

Private Async Sub GetNameAsync()
      Dim myTask As Task( Of String ) = AsyncMethod()
      Dim result As String = Await myTask
      Console.WriteLine( result )
  End Sub

底下的主控台範例程式展示了非同步與同步的程式寫法,C#語法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CSNew
{
    class Class1
    {
        static void Main(string[] args)
        {
            Console.WriteLine( "Sync demo started.." );
            GetNameSync( );
            Console.WriteLine( "Main finished" );
            Console.ReadLine( );

            Console.WriteLine( "Async demo started.." );
            GetNameAsync ();
            Console.WriteLine( "Main finished" );
            Console.ReadLine();
        }
        public static async Task< string > AsyncMethod()     
        {
            await Task.Delay( 1000 );
            return "Hello";
        }
        public static async Task< string > SynchMethod()
        {
            Thread.Sleep( 1000 );
            return "Hello";
        }
        private static async void GetNameSync()
        {
            Task< string > myTask = (Task< string >) SynchMethod();
            string result = await myTask;
            Console.WriteLine( result );
        }
        private static async void GetNameAsync()
        {
            Task< string > myTask = (Task< string >) AsyncMethod();
            string result = await myTask;
            Console.WriteLine( result );
        }
    }
}

非同步方法中若沒有使用await關鍵字,編譯程式時,Visual Studio 11 Beta會顯示警告訊息,但不代表錯誤,程式可以照常執行,不過,會被視為同步的方法來執行,例如範例中的SynchMethod方法。此程式執行的結果參考如下,參考圖20所示:

clip_image040

圖 20:async範例。

若改用Visual Basic程式語言, Async範例如下:

Imports System.Threading

Module Module1
    Sub Main()
        Console.WriteLine( "Sync demo started.." )
        GetNameSync( )
        Console.WriteLine( "Main finished" )

        Console.ReadLine( )

        Console.WriteLine( "Async demo started.." )
        GetNameAsync( )

        Console.WriteLine( "Main finished" )
        Console.ReadLine( )
    End Sub

    Private Async Sub GetNameSync()
        Dim myTask As Task( Of String ) = SynchMethod()
        Dim result As String = Await myTask
        Console.WriteLine( result )
    End Sub

    Private Async Sub GetNameAsync()
        Dim myTask As Task( Of String ) = AsyncMethod()
        Dim result As String = Await myTask
        Console.WriteLine( result )
    End Sub
    Public Async Function AsyncMethod() As Task( Of String )
        Await Task.Delay( 1000 )
        Return "Hello"
    End Function

    Public Async Function SynchMethod() As Task( Of String )
        Thread.Sleep( 1000 )
        Return "Hello"
    End Function
End Module

再者,async關鍵字可以套用在方法或Lambda運算式上,例如以下範例所示,C#語法:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace CSNew
{
    class Class1
    {
       static void Main ( string [ ] args )
        {
            Console.WriteLine("Async demo started..");
            GetNameAsync();
            Console.WriteLine("Main finished");
            Console.ReadLine();

        }
        private static async void GetNameAsync ( )
        {
            Func<Task> labexp = async ( ) =>
            {
                await Task.Delay(1000);
                Console.WriteLine("Hello");
            };
            await Run(labexp);
        }
        public static Task Run ( Func<Task> t )
        {
            if ( t != null )
                return Task.Run(t);
            return null;
        }
    }
}

Visual Basic語法:

Imports System.Threading

Module Module1
    Sub Main( )
        Console.WriteLine( "Async demo started.." )
        GetNameAsync( )
        Console.WriteLine( "Main finished" )
        Console.ReadLine( )
    End Sub

    Private Async Sub GetNameAsync( )
        Dim labexp As Func( Of Task ) = Async Function( )
                                          Await Task.Delay( 1000 )
                                          Console.WriteLine( "Hello" )
                                      End Function
        Await Run( labexp )
    End Sub
    Public Function Run( t As Func( Of Task ) )
        If ( t <> Nothing ) Then
            Return Task.Run( t )
        End If
        Return Nothing
    End Function
End Module

Task類別定義在System.Threading.Tasks命名空間下,代表一個非同步的作業,範例中叫用Run方法來執行Func<Task >,它可以用來呼叫一個沒有參數、會回傳一個Task型別的方法。

 

Visual Basic新語法-Iterator

Iterator通常都是應用在巡覽集合或陣列中的項目,C#在2005年Visual Studio 2005的版本中就已經支援,而Visual Basic直到Visual Studio 11 Beta 版才新增了Iterator語法 - Yield。

我們先參考一個簡單的iterator範例,定義一個列舉方法 (Iterator method) - GetName,此方法定義時,前方標示了Iterator Modifer,方法中利用Yield關鍵字,依序回傳「Mary」、「Candy」、「Lily」以及「Sandy」等字串。Main方法中利用For Each..Next方法列舉GetName方法回傳的結果。

Module Module1
    Sub Main( )
        For Each n As String In GetName( )
            Console.Write( n & "," )
        Next
    End Sub
    Public Iterator Function GetName( ) As System.Collections.IEnumerable
        Yield "Mary"
        Yield "Candy"
        Yield "Lily"
        Yield "Sandy"
    End Function
End Module
支援泛型型別

列舉方法 (Iterator method)的回傳型別也可以是一個泛型型別,例如底下範例,GetName方法回傳的是System.Collections.Generic.IEnumerable(Of String)型別。

Module Module1
    Sub Main( )
        For Each n As String In GetName( )
            Console.Write( n & "," )
        Next
    End Sub
    Public Iterator Function GetName( ) As System.Collections.Generic.IEnumerable( Of String )
        Yield "Mary"
        Yield "Candy"
        Yield "Lily"
        Yield "Sandy"
    End Function
End Module

支援屬性語法

Iterator Modifer除了可以應用在Function之外,也可以應用在屬性的語法上。例如以下範例Module1中GetNames屬性內宣告一個做為iterator的Get存取子(Get Accessor)。Main方法中則利用for each迴圈列出屬性中的所有字串。

Module Module1
    Sub Main( )
        For Each n As String In Module1.GetNames( )
            Console.Write( n & "," )
        Next
    End Sub

    Public ReadOnly Iterator Property GetNames( ) As IEnumerable( Of String )
        Get
            Yield "Mary"
            Yield "Candy"
            Yield "Lily"
            Yield "Sandy"
        End Get
    End Property
End Module


自訂類別

自訂的類別若需要實作列舉功能,可以實作IEnumerable介面,它包含一個GetEnumerator方法,只要實作這個方法就可以了。例如以下範例,EmployeeNameList類別實作IEnumerable介面,EmployeeNameList類別的建構函式建立了代表名稱的字串加入nameList集合中,GetEnumerator方法則透過for each搭配yield語法,一次傳回集合中的一個字串:

Module Module1
    Sub Main( )
        Dim empList As New EmployeeNameList ()
        For Each n As String In empList
            Console.Write( n & "," )
        Next
    End Sub
End Module

Public Class EmployeeNameList
    Implements IEnumerable
    Dim nameList As New List( Of String )
    Sub New()
        nameList.Add( "Mary" )
        nameList.Add( "Candy" )
        nameList.Add( "Lily" )
        nameList.Add( "Sandy" )
    End Sub
    Public Iterator Function GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
        For Each n In nameList
            Yield n
        Next
    End Function
End Class

匿名方法

Visual Basic中的匿名方法也可以是一個列舉方法,例如以下定義的匿名方法前方加註了Iterator關鍵字,方法中使用Yield一次回傳一個字串[P1]

Dim nameList = Iterator Function( ) As IEnumerable( Of String )
                           Yield "Mary"
                           Yield "Candy"
                           Yield "Lily"
                           Yield "Sandy"
                       End Function

        For Each n As String In nameList.Invoke( )

            Console.Write( n & "," )
        Next


呼叫階層(Call Hierarchy)

Visual Basic新增了呼叫階層(Call Hierarchy)視窗,這個功能在前一版Visual Studio 2010的C#開發工具中便已提供。由於Visual Studio 11 Beta 的方案總管(Solution Explorer)視窗也有類似「呼叫階層」視窗的功能,因此這項新功能似乎不太令人驚豔了,參考圖21所示。

clip_image042

圖 21:Visual Basic 的「Call Hierarchy」視窗。

 
Global關鍵字

Global關鍵字可以讓你存取到.NET的System命名空間,在你自訂命名空間和System下命名空間名稱相衝突時非常的有用。例如以下範例,自訂的A命名空間下包含一個子System命名空間。此命名空間和.NET Framework的System命名空間名稱相同,造成衝突。

Namespace A
    Namespace System
        Class Test
            Sub Print( )
                System.Console.WriteLine( "Hello" )
            End Sub
        End Class
    End Namespace
End Namespace

因此,當你在Test類別中,撰寫System.Console.WriteLine這行程式碼時,Visual Studio便會出現錯誤訊息,無法識別System下的Console類別,參考圖22所示:

clip_image044

圖 22:Global關鍵字可以解決名稱衝突。

我們可以將程式碼變更為改用Global關鍵字存取到.NET Framework中的System命名空間來解決這個命名空間衝突的問題:

Namespace A
    Namespace System
        Class Test
            Sub Print( )
                Global.System.Console.WriteLine( "Hello" )
            End Sub
        End Class
    End Namespace
End Namespace

沒有留言:

總瀏覽量