2019年8月3日 星期六

為何需要 .NET Standard

為何需要 .NET Standard

對於 .NET Standard 這樣的一個開發技術,相信對於大多數的開發者而言,應該會有很多的困惑,微軟為什麼需要提出這樣的技術呢?若身為開發者的你,僅僅在單一開發框架下進行專案開發,也許會對於這樣的技術感受不到任何該技術帶來的好處,但是,只要你使用 .NET 開發程式語言進行跨平台的專案開發的時候,就會知道這樣的開發技術所帶來的好處;就算你都在 .NET Framework 平台下來開發專案,使用 .NET Standard 技術,也會為你帶來相當的開發優勢與好處的。

了解更多關於 [.NET 標準 Standard




首先,.NET Standard 就有點像是 HTML 這樣的標準規格,只要你的瀏覽器有依據 HTML 規格進行開發,例如,你自製的瀏覽器有支援 HTML5,那麼,你開發的瀏覽器軟體就可以顯示出使用 HTML5 標準規格所設計的網頁內容。而 .NET Standard 也是相同,他是一個 .NET API 標準規格,在 .NET Standard 的不同版本內,定義了他支援了那些 .NET API與類別,你可以建立一個 .NET Standard 2.0 的類別庫,只要你所在的任何開發平台是 .NET Standard 2.0 所支援到的,那麼,就可以直接在該該平台專案下直接參考 Reference 這個 .NET Standard 2.0 類別庫組件,並且可以使用該類別庫內的相關類別與 API。
其實,.NET Standard 另外一個很重要的目的,那就是只要你所建立的類別庫,指定使用 .NET Standard 來進行設計,只要你選擇的 .NET Standard 版本所支援的 .NET 開發平台有支援,那麼,就可以直接在這些開發平台下,加入這個 .NET Standard 類別庫組件,便可以直接無誤的使用這個類別庫所設計的相關類別與 API。
有些人認為不使用 .NET Standard 所建立的類別庫,也可以在不同的開發平台下來進行參考與使用,其實這是會有問題的喔,接下來將會使用 .NET Core 2.2 / .NET Framework 4.7.2 / .NET Framework 4.0 開發平台做為測試範例。
在這篇文章所提到的專案原始碼,可以從 GitHub 下載

建立一個 .NET Core 2.2 類別庫

首先先來建立一個 .NET Core 2.2 類別庫,名為 ClassLibraryNETCore22。在這個類別庫專案內,將會建立一個 Class1 的類別,裡面僅有一個 DoSomething 方法,如底下程式碼所示。
這個 API File.WriteAllTextAsync() 將會在 .NET Core 2.2 下有支援,而且 在 .NET Core 2.2 下,可以有底下兩種同步與非同步的 WriteAllText 方法可以選擇 void WriteAllText(string path, string contents)與 Task WriteAllTextAsync(string path, string contents, CancellationToken cancellationToken = default);
C Sharp / C#
public class Class1
{
    public async Task DoSomething()
    {
        await File.WriteAllTextAsync("MyFile", "MyContent");
    }
}

建立一個 .NET Core 2.2 Console 專案,並且加入參考 .NET Core 2.2 類別庫

現在建立起一個 .NET Core 2.2 Console 專案,名為 ConsoleNETCore22,因此,這樣的使用方式是沒有問題的,也就是在該專案使用剛剛建立的類別庫內的類別,當然,這個 ClassLibraryNETCore22 類別庫是可以加入到 .NET Core 2.2 的專案裡面,並且建立起一個 Class1 類別的執行個體,使用執行個體方法 DoSomething 來呼叫,一切都是可以正常運作的。
C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        var foo = new ClassLibraryNETCore22.Class1().Wait();
        foo.DoSomething();
    }
}

建立一個 .NET Framework 4.7.2 Console 專案,並且加入參考 .NET Core 2.2 類別庫

既然現在有個 .NET Core 類別庫專案,或者說 .NET Core 的類別庫組件,很多人認為既然都是 .dll 檔案,當然是可以加入到任何 .NET 專案中來使用,現在,先來建立起一個 .NET Framework 4.7.2 Console 專案,名為 ConsoleNETFramework472,接著,在這個 Console 專案加入剛剛建立的 .NET Core 類別庫專案,看看會有甚麼情況發生。
一旦在 .NET Framework 4.7.2 的專案,加入參考 .NET Core 的類別庫專案,此時,將會看到底下的錯誤訊息
專案 '..\ClassLibraryNETCore22\ClassLibraryNETCore22.csproj' 以 'netcoreapp2.2' 為目標,無法供目標為 '.NETFramework,Version=v4.7.2' 的專案參考。    ConsoleNETFramework472
喔喔,竟然連建置這個程序都無法正常執行,直接噴出錯誤,雖然都是 .dll 組件檔案,不過,因為其開發框架的類型不相同,導致無法使用,所以,若想要使用 .NET Core 開發平台,建立起一系列的類別庫,讓其他 .NET 開發平台來使用,這樣的操作是行不同的。

建立一個 .NET Framework 4.7.2 類別庫

可是,若反過來建立一個 .NET Framework 4.7.2 類別庫專案,這個專案是否可以再 .NET Core 中順利使用呢?答案是不一定,因為在 .NET Framework 內的 API,有很多是在 .NET Core 開發平台下不支援的,或者在某些情境下會發生問題的。
現在,建立一個 .NET Framework 4.7.2 類別庫專案,名為 ClassLibraryNETFramework472 ,在這個專案內,同樣的有建立一個 Class1 類別,在這個裡面有兩個方法: DoSomething() , DoWebSomething() 。其中,DoSomething() 等下會進行測試,可以正常在 .NET Framework 4.7.2 平台下運行,可以,卻無法在 .NET Core 平台下運行(不過,可以正常建置與執行);而 DoWebSomething() 方法是用來在不同 .NET Framework 版本下用來測試,使用 .NET Framework 建立起來的類別庫,其相關 API 是否可以在不同的 .NET Framework 平台下來運行。
C Sharp / C#
public class Class1
{
    public void DoSomething()
    {
        File.WriteAllText("MyFile", "MyContent");
        var foo = System.Text.Encoding.GetEncoding(1252);
    }
    public void DoWebSomething()
    {
        // 在 .NET Framework 下,HttpClient 需要在 .NET Framework 4.5以上才有支援
        // void WriteAllText(string path, string contents);
        //
        HttpClient client = new HttpClient();
    }
}

.NET Framework 4.7.2 Console 專案,加入參考 .NET Framework 4.7.2 類別庫

請將剛剛建立好的 .NET Framework 4.7.2 專案 (ConsoleNETFramework472) ,確認該專案只有加入 .NET Framework 4.7.2 類別庫的專案 (ClassLibraryNETFramework472)。最後,在此 Console 專案內的 Main 方法內,加入底下的敘述。
現在,請建置與執行這個專案,這裡是可以正常運行無誤。
C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        var foo = new ClassLibraryNETFramework472.Class1();
        foo.DoSomething();
    }
}

.NET Core Console 專案,加入參考 .NET Framework 4.7.2 類別庫

現在,請在 .NET Core Console 專案內,加入參考 .NET Framework 4.7.2 類別庫 (ClassLibraryNETFramework472) ,並且在該 Console Main 方法內,修改成為底下的程式碼。
首先,請嘗試建置這個 .NET Core Console 專案,此時,可以順利正常建置成功
然後,現在開始執行這個 .NET Core Console 專案,不過,卻得到底下的錯誤訊息;所以,若想要建立 .NET Framework 的類別庫,想要讓其他平台專案下來使用,也是不可行的,特別要注意的是,許多 .NET Framework 的 API,在 .NET Core 是不支援的,可以參考 ApiCompat 有更多的說明資訊。
System.NotSupportedException: 'No data is available for encoding 1252. For information on defining a custom encoding, see the documentation for the Encoding.RegisterProvider method.'
C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        var foo = new ClassLibraryNETCore22.Class1();
        foo.DoSomething();
        var bar = new ClassLibraryNETFramework472.Class1();
        bar.DoSomething();
    }
}

建立一個 .NET Framework 4.0 Console 專案

現在,要來測試這個問題,若使用 .NET Framework 4.7.2 建立的類別庫專案,是否可以用於其他版本的 .NET Framework 專案下,例如,是否可以再 .NET Framework 4.0 Console 專案內呢?
現在請建立一個 .NET Framework 4.0 Console 專案,名為 ConsoleNETFramework40 ,並且將 .NET Framework 4.7.2 的類別庫 ( ClassLibraryNETFramework472 ) 加入參考到這個專案內。
請直接開始建置這個專案(雖然 Main 方法內還沒有加入其他程式碼),將會得到底下的警告訊息,不過,卻是可以正常建置成功的。
無法解析主要參考 "D:\Vulcan\GitHub\CSharp2019\WhyNeedNETStandard\ClassLibraryNETFramework472\bin\Debug\ClassLibraryNETFramework472.dll",因為它在架構組件 "System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" 上有間接相依性,但此組件無法在目前設為目標的架構中解析。".NETFramework,Version=v4.0"。若要解決此問題,請移除參考 "D:\Vulcan\GitHub\CSharp2019\WhyNeedNETStandard\ClassLibraryNETFramework472\bin\Debug\ClassLibraryNETFramework472.dll",或將應用程式的目標重定為包含 "System.Net.Http, Version=4.2.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" 的架構版本。    ConsoleNETFramework40            

參考的專案 'ClassLibraryNETFramework472' 目標設定的 Framework 版本 (4.7.2) 比這個專案的目前目標 Framework 版本 (4.0) 高。如果相依性鏈結中的任何專案使用這個專案的目標 Framework 以外的組件中的類型,可能導致組建失敗。    ConsoleNETFramework40            

無法解析主要參考 "D:\Vulcan\GitHub\CSharp2019\WhyNeedNETStandard\ClassLibraryNETFramework472\bin\Debug\ClassLibraryNETFramework472.dll",因為它是針對 ".NETFramework,Version=v4.7.2" 架構所建置。這個版本高於目前的目標架構 ".NETFramework,Version=v4.0"。    ConsoleNETFramework40
請修改 Main 方法的程式碼如下所示,接著,就可以再度建置這個專案,同樣的沒有發現到任何錯誤訊息,還是只有這3個警告訊息。
當要執行這個專案的時候,卻發生了底下的錯誤
從 Visual Studio 輸出視窗可以看到這個錯誤訊息
error CS0246: 找不到類型或命名空間名稱 'ClassLibraryNETFramework472' (是否遺漏了 using 指示詞或組件參考?)
所以,苦以驗證這樣的作法也是不行的
C Sharp / C#
class Program
{
    static void Main(string[] args)
    {
        var class1 = new ClassLibraryNETFramework472.Class1();
        class1.DoWebSomething();
    }
}

結論

想要設計一系列的類別庫,並且可以讓這些類別庫在不同 .NET 平台下來使用,最好的做法就是建立一個 .NET Standard 類別庫

了解更多關於 [.NET 標準 Standard






沒有留言:

張貼留言