可移植类库 (PCL)

提示

在最新版本的 Visual Studio 中,可移植类库 (PCL) 被视为已弃用。 尽管仍可以打开、编辑和编译 PCL,但对于新项目,建议使用 .NET Standard 库以访问更大的 API 外围应用。

构建跨平台应用程序的关键组件是跨各种特定于平台的项目共享代码的能力。 但是,由于不同的平台通常使用 .NET 基类库 (BCL) 的不同子集,因此它们实际上构建到不同的 .NET Core 库配置文件,这就很复杂。 这意味着每个平台只能使用面向同一配置文件的类库,因此它们对于每个平台似乎需要单独的类库项目。

主要有三种解决此问题的代码共享方法:.NET Standard 项目共享资产项目可移植类库 (PCL) 项目

  • .NET Standard 项目是共享 .NET 代码的首选方法,请阅读有关 .NET Standard 项目和 Xamarin 的详细信息。
  • 共享资产项目使用单个文件集,并提供一种快速而简单的方法来在解决方案中共享代码,并通常使用条件编译指令为将使用它的各种平台指定代码路径(有关详细信息,请参阅共享项目一文)。
  • PCL 项目面向支持一组已知 BCL 类/功能的特定配置文件。 但是,PCL 的缺点是,它们通常需要额外的体系结构工作,然后才能将配置文件特定代码分成自己的库。

本页介绍如何创建面向特定配置文件的 PCL 项目,它们可由多个特定于平台的项目引用。

什么是可移植类库?

创建应用程序项目或库项目时,生成的 DLL 仅限于在创建时面向的特定平台上工作。 这让你无法为 Windows 应用编写一个程序集,然后在 Xamarin.iOS 和 Xamarin.Android 上重新使用它。

但是,创建可移植类库时,可以选择一个你想要运行代码的平台的组合。 创建可移植类库时所做的兼容性选择将转换为“配置文件”标识符,它描述了库支持的平台。

下表显示了一些因 .NET 平台而异的功能。 若要编写保证在特定设备/平台上运行的 PCL 程序集,只需在创建项目时选择所需的支持。

功能 .NET Framework UWP 应用 Silverlight Windows Phone Xamarin
核心 Y Y Y Y Y
LINQ Y Y Y Y Y
IQueryable Y Y Y 7.5+ Y
序列化 Y Y Y Y Y
数据注释 4.0.3 + Y Y Y

Xamarin 列反映了 Xamarin.iOS 和 Xamarin.Android 支持 Visual Studio 附带的所有配置文件,并且你创建的任何库中的功能的可用性将仅受你选择支持的其他平台的限制。

这包括以下组合的配置文件:

  • .NET 4 或 .NET 4.5
  • Silverlight 5
  • Windows Phone 8
  • UWP 应用

你可以在 Microsoft 网站上阅读有关不同配置文件功能的详细信息,并查看其他社区成员的 PCL 配置文件摘要,其中包括支持的框架信息和其他说明。

优点

  1. 集中式代码共享 - 在其他库或应用程序可以使用的单个项目中编写和测试代码。
  2. 重构操作将影响解决方案中加载的所有代码(可移植类库和特定于平台的项目)。
  3. PCL 项目可由解决方案中的其他项目轻松引用,也可以共享输出程序集以供其他人在其解决方案中引用。

缺点

  1. 由于同一可移植类库在多个应用程序之间共享,因此无法引用特定于平台的库(例如 Community.CsharpSqlite.WP7)。
  2. 可移植类库子集可能不包含在 MonoTouch 和 Mono for Android 中本来可用的类(例如 DllImport 或 System.IO.File)。

注意

Visual Studio 的最新版本中已弃用可移植类库,建议改用 .NET Standard 库

在某种程度上,可以使用提供程序模式或依赖项注入来针对可移植类库中定义的接口或基类对平台项目中的实际实现进行编码,从而规避这两个缺点。

此图显示了使用可移植类库共享代码的跨平台应用程序的体系结构,它还使用了依赖项注入来传入依赖于平台的功能:

This diagram shows the architecture of a cross-platform application using a Portable Class Library to share code, but also using Dependency Injection to pass in platform-dependent features

Visual Studio for Mac 演练

本部分介绍如何使用 Visual Studio for Mac 创建和使用可移植类库。 有关完整实现,请参阅 PCL 示例部分。

创建 PCL

将可移植类库添加到解决方案与添加常规库项目非常相似。

  1. 在“新建项目”对话框中,选择“多平台 > 库 > 可移植库”选项:

    Create a new PCL project

  2. 在 Visual Studio for Mac 中创建 PCL 时,会自动使用适用于 Xamarin.iOS 和 Xamarin.Android 的配置文件配置它。 PCL 项目将出现,如屏幕截图所示:

    PCL project in the solution pad

PCL 现已准备好添加代码。 它也可以由其他项目(应用程序项目、库项目甚至其他 PCL 项目)引用。

编辑 PCL 设置

若要查看和更改此项目的 PCL 设置,请右键单击该项目,然后选择“选项 > 生成 > 常规”以查看如下所示的屏幕:

PCL Project Options to set the profile

单击“更改...”可更改此可移植类库的目标配置文件。

如果在代码已添加到 PCL 后更改配置文件,则如果代码引用了不属于新选的配置文件的功能,库可能不会再编译。

使用 PCL 进行工作

在 PCL 库中编写代码时,Visual Studio for Mac 编辑器将识别所选配置文件的限制,并相应地调整自动完成选项。 例如,此屏幕截图显示了使用 Visual Studio for Mac 中使用的默认配置文件 (Profile136) 的 System.IO 自动完成选项 - 请注意,滚动条指示大约有一半的可用类被显示(实际上只有 14 个类可用)。

Intellisense list of 14 classes in the System.IO class of a PCL

将其与 Xamarin.iOS 或 Xamarin.Android 项目中的 System.IO 自动完成进行比较 - 有 40 个类可用,包括常用的类,例如 FileDirectory,它们不在任何 PCL 配置文件中。

Intellisense list of 40 classes in .NET Framework System.IO namespace

这反映了使用 PCL 时基本的权衡 - 能够跨多个平台无缝共享代码意味着某些 API 不可用,因为它们不是在所有可能的平台中都有相应的实现。

使用 PCL

创建 PCL 项目后,可以按照通常添加引用的方式从任何兼容的应用程序或库项目添加对其的引用。 在 Visual Studio for Mac 中,右键单击“引用”节点并选择“编辑引用...”,然后切换到“项目”选项卡,如下所示:

Add a reference to a PCL via Edit References option

以下屏幕截图显示了 TaskyPortable 示例应用的解决方案面板,其中显示了底部的 PCL 库,以及对 Xamarin.iOS 项目中该 PCL 库的引用。

TaskyPortable sample solution showing PCL project

PCL 的输出(即生成的程序集 DLL)也可以添加为对大多数项目的引用。 这使得 PCL 成为交付跨平台组件和库的理想方法。

PCL 示例

TaskyPortable 示例应用程序演示如何将可移植类库与 Xamarin 配合使用。 下面是 iOS 和 Android 上运行的生成应用的一些屏幕截图:

Here are some screenshots of the resulting apps running on iOS, Android and Windows Phone

它共享许多属于纯可移植代码的数据和逻辑类,还演示了如何使用 SQLite 数据库实现的依赖项注入来整合特定于平台的要求。

解决方案结构如下所示(分别在 Visual Studio for Mac 和 Visual Studio 中):

The solution structure is shown here in Visual Studio for Mac and Visual Studio respectively

由于 SQLite-NET 代码具有特定于平台的部分(用于处理每个不同操作系统上的 SQLite 实现),因此它已重构为可编译为可移植类库的抽象类,而实际代码作为 iOS 和 Android 项目中的子类实现。

TaskyPortableLibrary

可移植类库在它可以支持的 .NET 功能中受限。 由于它编译为在多个平台上运行,因此它不能使用 SQLite-NET 中使用的 [DllImport] 功能。 相反,SQLite-NET 实现为抽象类,然后通过共享代码的其余部分引用它。 抽象 API 的提取如下所示:

public abstract class SQLiteConnection : IDisposable {

    public string DatabasePath { get; private set; }
    public bool TimeExecution { get; set; }
    public bool Trace { get; set; }
    public SQLiteConnection(string databasePath) {
         DatabasePath = databasePath;
    }
    public abstract int CreateTable<T>();
    public abstract SQLiteCommand CreateCommand(string cmdText, params object[] ps);
    public abstract int Execute(string query, params object[] args);
    public abstract List<T> Query<T>(string query, params object[] args) where T : new();
    public abstract TableQuery<T> Table<T>() where T : new();
    public abstract T Get<T>(object pk) where T : new();
    public bool IsInTransaction { get; protected set; }
    public abstract void BeginTransaction();
    public abstract void Rollback();
    public abstract void Commit();
    public abstract void RunInTransaction(Action action);
    public abstract int Insert(object obj);
    public abstract int Update(object obj);
    public abstract int Delete<T>(T obj);

    public void Dispose()
    {
        Close();
    }
    public abstract void Close();

}

共享代码的其余部分使用抽象类从数据库中“存储”和“检索”对象。 在任何使用此抽象类的应用程序中,都必须传入提供实际数据库功能的完整实现。

TaskyAndroid 和 TaskyiOS

iOS 和 Android 应用程序项目包含用户界面和其他特定于平台的代码,用于连接 PCL 中的共享代码。

这些项目还包含适用于该平台的抽象数据库 API 的实现。 在 iOS 和 Android 上,Sqlite 数据库引擎内置于操作系统中,因此该实现可使用所示的 [DllImport] 来提供数据库连接的具体实现。 此处显示了特定于平台的实现代码的摘录:

[DllImport("sqlite3", EntryPoint = "sqlite3_open")]
public static extern Result Open(string filename, out IntPtr db);

[DllImport("sqlite3", EntryPoint = "sqlite3_close")]
public static extern Result Close(IntPtr db);

可以在示例代码中看到完整的实现。

总结

本文简要讨论了可移植类库的优点和缺陷,演示了如何从 Visual Studio for Mac 和 Visual Studio 内部创建和使用 PCL。最后介绍了一个完整的示例应用程序 - TaskyPortable,它展示了 PCL 的实际应用。