XAML in Xamarin.Forms 基礎篇 電子書

XAML in Xamarin.Forms 基礎篇 電子書
XAML in Xamarin.Forms 基礎篇 電子書

Xamarin.Forms 快速入門 電子書

Xamarin.Forms 快速入門 電子書
Xamarin.Forms 快速入門 電子書

2017/09/24

了解 .NET 標準類別庫 (.NET Standard) 的 Type Forwarding 運作情形(模擬演練)

在 .NET 標準類別庫 (.NET Standard Class Library) 裡面,使用到一個核心技術,那就是 Type Forwarding。
我們在這裡將會透過實際演練過程,讓您體驗出何謂 Type Forwarding。

準備專案

在這裡,建立一個 .NET Framework 類別庫,我們命名為:MyLibAssemblyA,這個類別庫內,僅有一個類別宣告,如下所示:
namespace TypeForward
{
    public class Foo
    {
    }
}
接著,我們建立一個 .NET Framework 可執行的專案,我們命名為:TypeForward,並且在這個專案內,使用加入參考功能,將類別庫 MyLibAssemblyA 加入到這專案內,這個可執行專案的程式碼如下:
using System;

namespace TypeForward
{
    class Program
    {
        static void Main(string[] args)
        {
            Foo foo = new Foo();
            Console.WriteLine(typeof(Foo).AssemblyQualifiedName);
            Console.WriteLine("Press any key for continuing...");
            Console.ReadKey();
        }
    }
}

第一次執行

此時,請設定可執行的 .NET Framework 專案,TypeForward,為預設起始專案,並且執行這個專案。
您會看到這個應用程式將會輸出如下的內容,我們從這個內容,可以看到,我們這裡使用的型別, Foo 是位於 MyLibAssemblyA 組件內。
TypeForward.Foo, MyLibAssemblyA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Press any key for continuing...

加入 Type Forwarding

我們準備要把 Foo 這個類別的宣告,搬移到另外一個組件 (Assembly) 中,所以,我們另外建立一個 .NET Framework 類別庫,我們命名為:MyLibAssemblyB,這個類別庫內,僅有一個類別宣告,如下所示:
namespace TypeForward
{
    public class Foo
    {

    }
}
接下來,我們回到 MyLibAssemblyA 專案內,將之前宣告 Foo 類別的 .cs 檔案,修改成為如下的程式碼。
在這裡,我們看到了,我們將類別 Foo 的宣告已經移除了 (此時,這個類別的宣告已經搬移到 MyLibAssemblyB 專案內。
並且,我們在 MyLibAssemblyA 專案內,使用屬性 (Attribute) TypeForwardedTo 來指定目的地型別 Type (Foo) 其他組件(MyLibAssemblyB)中。
最後,我們要在 MyLibAssemblyA 專案中,使用加入參考功能,將 MyLibAssemblyB 專案加入到其參考清單中。
+

現在,我們可以重新建置這個 MyLibAssemblyA 專案。
請不要去建置 .NET Framework 可執行的專案 TypeForward
using System.Runtime.CompilerServices;

[assembly: TypeForwardedTo(typeof(TypeForward.Foo))]
//namespace TypeForward
//{
//    public class Foo
//    {
//    }
//}

第二次執行 (使用 Type Forwarding)

現在,我們到 MyLibAssemblyA 專案目錄下的 TypeForward\MyLibAssemblyA\bin\Debug 內的兩個 .dll 與兩個 .pdb 檔案都複製下來。
接著,將剛剛複製下來的四個檔案,複製到 .NET Framework 可執行的專案 TypeForward 下的 TypeForward\TypeForward\bin\Debug 目錄中。
此時,請執行 TypeForward.exe 檔案,您就會看到底下輸出結果。
這個時候,我們將看到了,類別 Foo 已經從組件 MyLibAssemblyA 轉移到組件 MyLibAssemblyB 了。
TypeForward.Foo, MyLibAssemblyB, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null
Press any key for continuing...

2017/09/22

剖析 .NET Framework / .NET Core / Xamarin / PCL / .NET Standard 類別庫的演進歷程

現在的 .NET 開發環境已經是百花齊放,除了可以在 Windows 平台下開發出可在 .NET Framework 的應用程式,還可以在 Linux、macOS、UWP、Android、iOS 等不同作業系統下,使用 .NET 技術進行應用程式的開發。

了解更多關於 [Xamarin.Android] 的使用方式
了解更多關於 [Xamarin.iOS] 的使用方式
了解更多關於 [Xamarin.Forms] 的使用方式
了解更多關於 [Hello, Android:快速入門] 的使用方式
了解更多關於 [Hello, iOS – 快速入門] 的使用方式
了解更多關於 [Xamarin.Forms 快速入門] 的使用方式

 不過,若要能夠讓我們開發出來的類別可以在不同專案或者不同平台下能夠做到共用與分享,該使用甚麼樣的方法呢?在本篇文章中,我們將會進行不同角度的探討與測試。

只有 Windows 作業系統環境下

在以往的 .NET 生態系統中,若想要開發出 .NET 應用程式,此時,就只能選擇在 Windows 作業系統下來執行這個應用程式,並且,所使用的 .NET 運行生態,也就是我們常在使用的 .NET Framework 透過 .NET Framework 的開發環境,我們可以使用 BCL (基底類別庫 Base Class Library) / FCL (框架類別庫 Framework Class Library) 所提供豐富的類別,進行開發出 Windows Forms / WPF / ASP.NET Web Forms / ASP.NET MVC 這樣不同類型的應用程式。
當然,這段時期,也有使用 .NET Framework 子集合環境所創造出來的,如:Silverlight、Windows Phone 7.x、.NET Compact等等,不過,由於這些環境已經不再支援,所以,我們將不會進行討論

在專案之間共用類別庫

在 .NET Framework 開發環境下,我們可以設計一個屬於自己環境使用的類別庫 Class Library (這裡會加入我們自行開發的不同類別、介面、委派等型別與不同商業邏輯 API) 專案,經過建置之後,就會成為一個類別庫組件 Class Library Assembly;之後,我們可以讓不同的 .NET Framework 專案,將這個類別庫專案的參考,加入到正在開發的專案內,如此,便可以共享與共用這個類別庫裡面所設計出來的各種商業邏輯與類別。
由於我們透過類別庫進行設計出自己或者公司需要用到的各種不同類別與功能和計算能力,全部整合在一個或者多個組件 Assembly 內,一旦這些商業邏輯或者類別有修正的時候,我們就僅僅需要維護一套類別庫專案原始碼即可,就可以將修正後的類別庫組件讓其他專案來更新與使用。這就是 .NET 類別庫的主要目的,可以讓您共享自行開發的 .NET 類別於不同專案上來使用。

.NET 可以跨平台了 Windows / Linux / macOS / Android / iOS / UWP

現在,在 .NET 生態系統中,除了可以在 Windows 作業系統下執行,也可以在 Linux / macOS / Android / iOS / UWP 系統下開發出可以執行 .NET 應用程式。因此,在 .NET 生態系統中,除了以往的 .NET Framework 之外,另外增加了 .NET Core 與 Xamarin 這兩個開發生態環境。
在這三個 .NET 開發生態環境中,.NET Framework / .NET Core / Xamarin,他們都各自有屬於自己定義出來的 BCL (基底類別庫 Base Class Library) 與通用語言執行階段 CLR (Common Language Runtime) ,最重要的是,這些 BCL 並不是在每個平台下都有完整的設計與實作出來(最完整的 .NET 基底類別庫,則是完全實作在 .NET Framework生態環境中)。
現在,我們遇到一個問題,當我們需要設計出屬於自己會重複使用到的類別庫,要共享這些程式碼到不同平台、不同專案下,該如何進行設計呢?

在每個 .NET 平台建立屬於該平台下的類別庫

我們可以在這三個 .NET 平台 (.NET Framework, .NET Core, Mono Xamarin)中,分別建立出這三個平台會用到的類別庫專案;如此,每個.NET平台下的不同專案,就可以參考該平台所建立的類別庫專案,這樣,就可以彼此互相共享類別與商業邏輯設計了。
使用這樣解決方案,你需要維護可以分別在 .NET Framework / .NET Core / Mono Xamarin 平台下的三套類別庫原始碼。
在這篇文章,將會透過 Visual Studio 2017 所建立一個方案,進行相關問題測試;在這個方案內,我們建立了許多類別庫與各平台的可執行專案,如下圖所示。
.NET Standard
在這個測試方案中,我們分別建立了
  • .NET Framework 類別庫專案
  • .NET Core 類別庫專案
  • Android 類別庫專案
  • PCL 可攜式專案 (這裡建立的是 Profile259 的可攜式專案類別庫)
  • .NET 標準類別庫專案 ( .NET Standard Class Library)
想要建立這些不同平台的類別庫,可以在 Visual Studio 2017 內,增加一個專案項目,當 新增專案 對話窗出現之後,你可以在右上方的搜尋條件文字輸入盒內,輸入 類別庫 C# 關鍵字,此時,對話窗終將會列出可以新建的類別庫專案類型,如下圖所示。
Visual Studio 新增項目
 你也許會想到說,幹嘛這麼麻煩,這很簡單,我們只需要建立一個 .NET Framework 的類別庫,並且將它的參考加入到 .NET Core / Xamarin 專案內,這樣,我們就可以透過 .NET Framework 所創建出來的類別庫,達到共享程式碼的目的了,而且我們也只需要維護一套類別庫原始碼。
所以,有時候,現實是殘酷的,這樣的想法與做法,還是無法解決我們跨平台專案共享程式碼的需求,在底下的文章內容,你將會看到這樣開發過程所產生的問題。

建立每個平台下的可執行專案

為了要能夠讓我們可以在不同的 .NET 平台下可以執行這些平台的應用程式,所以,我們接著透過 Visual Studio 2017 建立起不同平台的可執行專案:
  • NETFrameworkConsoleApp
    這是可以在 Windows 作業系統下的主控台應用程式
  • NETCoreConsoleApp
    這是可以在 Windows / Linux / macOS 作業系統下的主控台應用程式
  • AndroidApp
    這是可以在 Android 裝置下執行的行動應用程式 (Mobile App)
要建立上述不同 .NET 平台下的可執行專案,請在如下圖的 新增專案 對話窗中,選擇適當的專案類型即可。
.NET Framework / .NET Core / Android

設計 .NET Framework 類別庫,使其在 .NET Framework 下共用

在這裡,我們在 .NET Framework 的 NETFrameworkClassLibrary 類別庫專案內,設計一個類別,如下所示:
這個類別庫 (Class Library)專案, FrameworkClass, 設計的相當簡單,他只有一個屬性成員,並且有建立這個屬性的物件。這裡用到的類別 Bitmap 將會是由 .NET Framework 中的基底類別庫 (BCL) 中所提供的。
    public class FrameworkClass
    {
        public Bitmap MyProperty { get; set; } = new Bitmap(100, 100);
    }
接著,我們需要在 NETFrameworkConsoleApp 可執行專案下,加入 .NET Framework 類別庫的專案 NETFrameworkClassLibrary,如此,我們才可以在這個可執行專案內,使用這個 .NET Framework 類別庫內的自行開發設計的型別。
請在 Visual Studio 2017 內,在 NETFrameworkConsoleApp 專案加入參考
請參考下圖,加入 NETFrameworkClassLibrary 類別庫專案到這個可執行專案內
加入參考
由於,這個類別庫會參考用到了 BCL 的 System.Drawing 這個組件,所以,請在同樣的 參考管理員 對話窗內,點選 組件 頁次,接著勾選 System.Drawing 項目。
加入參考
好的,現在我們可以到 NETFrameworkConsoleApp 專案內,使用 NETFrameworkClassLibrary 類別庫內的 FrameworkClass 類別了。
我們在可執行專案內的 Program.cs檔案內之方法 Main 內,撰寫了底下程式碼,並且請您執行該專案;此時,您將不會得到任何例外異常錯誤,這個專案可以正常執行。
    class Program
    {
        static void Main(string[] args)
        {
            var fooObject = new NETFrameworkClassLibrary.FrameworkClass();
            var barObject = fooObject.MyProperty;
            Console.WriteLine("Hello World!");
            Console.WriteLine("Press any key for continuing...");
            Console.ReadKey();
        }
    }

共享 .NET Framework 類別庫給 .NET Core 的可執行專案

類別庫存在的目的,就是要能夠重複使用(避免重複設計,這樣也會避免因為重複的程式碼,造成更多出錯的機會)這些類別,因此,我們來將 .NET Framework 類別庫加入到 .NET Core 的專案內。
我們將 NETCoreConsoleApp 專案內,打開 參考管理員 - NETCoreConsoleApp 對話窗,找到 專案 頁次,勾選 NETFrameworkClassLibrary 項目,將這個專案參考加入到 NETCoreConsoleApp 專案內。
另外,所們在這裡 參考管理員 - NETCoreConsoleApp 似乎沒有看到如同 .NET Framework 平台下的 組件 頁次,可以讓我們選擇 System.Drawing 這個組件
Class Library 類別庫
在您加入完這類別庫參考之後,請重新開啟 參考管理員 - NETCoreConsoleApp,很不幸的,所們在這裡 參考管理員 - NETCoreConsoleApp 似乎沒有看到如同 .NET Framework 平台下的 組件 頁次,可以讓我們選擇 System.Drawing 這個組件。
在 .NET Core 中,現在還沒有支援這個 BCL 類別 Bitmap,所以,就算你可以正常把 .NET Framework 類別庫加入到 .NET Core 的可執行專案內,在 .NET Core 的專案內,還是無法正常建置與執行。
我們在 NETCoreConsoleApp 撰寫同樣的程式碼
    class Program
    {
        static void Main(string[] args)
        {
            var fooObject = new NETFrameworkClassLibrary.FrameworkClass();
            var barObject = fooObject.MyProperty;
            Console.WriteLine("Hello World!");
            Console.WriteLine("Press any key for continuing...");
            Console.ReadKey();
        }
    }
您會看到底下的錯誤訊息
錯誤    CS0012    類型 'Bitmap' 定義在未參考的組件中。您必須加入組件 'System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a' 的參考。    NETCoreConsoleApp    D:\Temp\NETClassLibrary\NETCoreConsoleApp\Program.cs    12    使用中
不過,若我們將 var barObject = fooObject.MyProperty; 這行程式碼註解起來,您將會發現到,這個 .NET Core 專案竟然可以建置成功了。
不過,不要高興得太早,我們將這個 .NET Core 可執行專案設定為預設起始專案,並且執行這個專案,您將會得到底下的執行時期錯誤訊息。
發生 System.IO.FileNotFoundException
  HResult=0x80070002
  Message=Could not load file or assembly 'System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'. 系統找不到指定的檔案。
  Source=<無法評估例外狀況來源>
  StackTrace: 
   at NETFrameworkClassLibrary.FrameworkClass..ctor() in D:\Temp\NETClassLibrary\NETFrameworkClassLibrary\FrameworkClass.cs:line 12
   at NETCoreConsoleApp.Program.Main(String[] args) in D:\Vulcan\GitHub\CSharpOOP\NETClassLibrary\NETCoreConsoleApp\Program.cs:line 11
FileNotFoundException
    class Program
    {
        static void Main(string[] args)
        {
            var fooObject = new NETFrameworkClassLibrary.FrameworkClass();
            //var barObject = fooObject.MyProperty;
            Console.WriteLine("Hello World!");
            Console.WriteLine("Press any key for continuing...");
            Console.ReadKey();
        }
    }
 所以,若我們想要設計出一個可以用於不同 .NET 平台都可以執行的類別庫專案,該如何解決此一問題呢?

想要在 .NET 跨平台開發環境下,建立各平台都可以用的類別庫

從剛剛的過程中,我們發現到,我們可以在各個平台 .NET Framework / .NET Core / Xamarin 下,建立自己平台的類別庫,這些類別庫,是可以在自己的平台下重複使用(只要加入這個類別庫到自己的專案內即可);可是,我們卻無法跨平台的將 A 平台的類別庫,加入到 B 平台專案內,例如上面個過程,將 .NET Framework 建立的類別庫,加入到 .NET Core 專案內來使用。
對於這樣跨平台的類別庫需求,.NET 生態系統提供了兩種選擇 可攜式類別庫 (PCL Portable Class Library與 .NET 標準類別庫 (.NET Standard Class Library)

第一代的跨平台類別庫 : PCL 可攜式類別庫

PCL 可攜式類別庫是早期用於 .NET 跨平台類別庫的共享技術,在您建立一個 PCL 可攜式類別庫 的時候,您需要指定這個 PCL 可攜式類別庫需要與那些平台共享,一旦決定可以共享的平台之後,在這個 PCL 可攜式類別庫 內,可以使用的類別,就是這些平台都有提供的類別與成員和方法。
PCL 可攜式類別庫
我們在這裡建立一個 Profile259 的 PCL 可攜式類別庫,可是,當我們要建立一個類別,裡面會有個 HttpClient 成員。
    public class PCL259Class
    {
        HttpClient client;
    }
您會看到底下錯誤訊息,竟然無法使用 HttpClient 這個類別,那麼,我們如同 .NET Framework 平台下,將這個 HttpClient 類別的組件加入進來,不就解決掉這個問題了嗎?不幸的是,當您打開了 參考管理員 - PCL259ClassLibrary 對話窗,切換到 組件 頁次,且是得到此訊息 所有 Framework 組件都已經被參考了,請使用物件瀏覽器瀏覽 Framework 中的參考。這是因為在 PCL 可攜式類別庫 內,採用的技術是將要支援的不同平台的已經存在的類別庫,以交集做運算,僅提供每個平台都有提供的類別,才能夠在 PCL 可攜式類別庫 內使用。
錯誤    CS0246    找不到類型或命名空間名稱 'HttpClient' (是否遺漏了 using 指示詞或組件參考?)    PCL259ClassLibrary    D:\Temp\NETClassLibrary\PCL259ClassLibrary\PCL259Class.cs    11    使用中

下一代的跨平台類別庫 : .NET 標準類別庫 (.NET Standard Class Library)

這是微軟針對 PCL 可攜式類別庫 所面臨到的困境,設計出來的跨平台類別庫設計方法。
他與 PCL 可攜式類別庫的不同在於:
  • PCL 可攜式類別庫
    是針對現有平台可以支援的 BCL 所有類別,與要支援平台,定義出一個 Profile#,例如,上面的例子中,我們建立一個 Profile259 的PCL 可攜式類別庫;在這個 Profile259 的 PCL 可攜式類別庫可以使用的類別,將會取其各平台 BCL 都有的類別,取其交集,這樣,就可以在 PCL 可攜式類別庫 使用這些類別。
  • .NET 標準類別庫 ( .NET Standard )
    .NET 標準類別庫的作法卻與PCL 可攜式類別庫完全不同。.NET 標準類別庫是一個規格,在.NET 標準類別庫訂出的一個新版本規格之後,所有平台 BCL 都需要能夠支援這個 .NET 標準類別庫的新標準,因此,只要這個新版本的.NET 標準類別庫有列出的類別,您就可以在 .NET 標準類別庫 內使用這些類別與型別。
    如此,當您設計好一個屬於自己的 .NET 標準類別庫後,就可以在不同平台下的專案,把這個 .NET 標準類別庫直接加入其參考,並且可以正常與順利使用與執行這些共用類別與相關 API。

建立一個各平台都可以用的類別庫

我們在這裡建立一個.NET 標準類別庫,NETStandardClassLibrary,並且將這個專案參考,加入到各平台專案內;若要建立一個.NET 標準類別庫,請在新增專案的時候,選擇如下圖的 .NET Standard 標籤頁次,接著選取 類別庫 (.NET Standard) 項目即可。
.NET Standard 標準類別庫
接著,我們在這個 NETStandardClassLibrary 專案內,設計底下 StandardClass,它會抓取微軟網站的內容,並且回傳這個網站內容字串。
      public static class StandardClass
    {
        public static async Task<string> GetContent()
        {
            HttpClient client = new HttpClient();
            var result = await client.GetStringAsync("http://www.microsoft.com");
            return result;
        }
    }
接著,我們分別在 .NET Framework / .NET Core 平台下的專案,使用底下程式碼來進行測試,結果是可以正常運作的。
        static void Main(string[] args)
        {
            var content = NETStandardClassLibrary.StandardClass.GetContent().Result;
            Console.WriteLine($"Web Content Length : {content.Length}");
            Console.WriteLine("Press any key for continuing...");
            Console.ReadKey();
        }

結論

若您想要開發出可以用於跨 .NET 不同平台專案下,可以共用的類別庫,請選擇建立與使用 .NET 標準類別庫。