Unity 和 UWP 中缺少的 .NET API

在使用 .NET 生成 UWP 游戏时,你可能会发现你在 Unity 编辑器中可能使用的或用于独立电脑游戏的一些 API 对于 UWP 不存在。 这是因为适用于 UWP 应用的 .NET 包括每个命名空间的完整 .NET Framework 中提供的类型子集。

此外,某些游戏引擎使用与适用于 UWP 的 .NET 不完全兼容的不同 .NET 风格,如 Unity 的 Mono。 因此在你编写游戏时,编辑器中可能完全正常,但当你针对 UWP 进行构建时,你可能会看到如下错误:类型或命名空间“格式化程序”在命名空间“System.Runtime.Serialization”中不存在(你是否缺少程序集引用?)

幸运的是,Unity 提供这些缺失的 API 的一部分作为扩展方法和替代类型,在通用 Windows 平台:在 .NET 脚本后端缺少的 .NET 类型对此进行了描述。 但是,如果你需要的功能不在此处,则适用于 Windows 8.x 应用的 .NET 概述中讨论了可转换代码以使用适用于 Windows 运行时 API 的 WinRT 或 .NET 的方法。 (它讨论的是 Windows 8,但也适用于 Windows 10 UWP 应用。)

.NET Standard

要了解一些 API 为什么无法工作,须了解不同的 .NET 风格以及 UWP 如何实现 .NET。 .NET Standard 是 .NET API 的正式规范,适用于各个平台,并统一不同的 .NET 风格。 .NET 的每个实现都支持特定版本的 .NET Standard。 你可以参阅 .NET 实现支持中的标准和实现表。

UWP SDK 的每个版本都符合不同级别的 .NET Standard。 例如,16299 SDK(秋季创作者更新)支持 .NET Standard 2.0。

如果你要了解特定的 .NET API 在你的目标 UWP 版本中是否受支持,可以查看 .NET Standard API 参考并选择该 UWP 版本支持的 .NET Standard 版本。

脚本后端配置

如果你在针对 UWP 进行生成时遇到麻烦,首先应该检查“播放器设置”(“文件”>“生成设置”,并依次选择“通用 Windows 平台”和“播放器设置”)。 在“其他设置配置”>下,前三个下拉列表 (脚本运行时版本脚本后端Api 兼容性级别) 都是要考虑的重要设置。

Unity 脚本后端使用脚本运行时版本来允许你获取你选择的 .NET Framework 支持的(大致)等效版本。 但请记住,该版本的 .NET Framework 中的所有 API 并非全部受支持,仅在你的 UWP 的目标 .NET Standard 版本中的 API 受支持。

在新发布的 .NET 中通常会将更多 API 添加到 .NET Standard 中,这可能允许你在独立版和 UWP 中使用相同代码。 例如,.NET Standard 2.0 中引入了 System.Runtime.Serialization.Json 命名空间。 如果你将脚本运行时版本设置为 .NET 3.5 等效项(针对较早版本的 .NET Standard),你在尝试使用 API 时会遇到错误;将其切换到 .NET 4.6 等效项(支持 .NET Standard 2.0),该 API 将正常运行。

脚本后端可以为 .NETIL2CPP。 在本主题中,我们假设你已选择 .NET,因为在此处讨论的问题是在这种情况下发生的。 请参阅脚本后端获取详细信息。

最后,你应将 API 兼容性级别设置为你要运行游戏的 .NET 版本。 它应该匹配脚本运行时版本

一般情况下,对于脚本运行时版本API 兼容性级别,你应该选择可用的最新版本,以便提高与 .NET Framework 的兼容性,你也可以使用更多的 .NET API。

配置:脚本运行时版本;脚本后端;API 兼容性级别

依赖于平台的编译

如果你正在为多个平台(包括 UWP)生成 Unity 游戏,你需要使用依赖于平台的编译,以确保仅当作为 UWP 生成游戏时运行适用于 UWP 的代码。 这样一来,你可以对独立桌面和其他平台使用完整的 .NET Framework,对 UWP 使用 WinRT API,不会发生生成错误。

使用以下指令,以便仅在作为 UWP 应用运行时编译代码:

#if NETFX_CORE
    // Your UWP code here
#else
    // Your standard code here
#endif

注意

NETFX_CORE仅用于检查是否要针对 .NET 脚本后端编译 C# 代码。 如果使用的是其他脚本后端(如 IL2CPP),请改用 ENABLE_WINMD_SUPPORT

常见问题和解决方法

以下方案描述当 UWP 子集中缺少 .NET API 时可能出现的常见问题以及解决方法。

使用 BinaryFormatter 进行数据序列化

游戏经常序列化保存数据,使玩家无法轻松操控它。 但是,将对象序列化为二进制的 BinaryFormatter 在 .NET Standard 的较早版本(2.0 以前)中不可用。 请考虑改用 XmlSerializerDataContractJsonSerializer

private void Save()
{
    SaveData data = new SaveData(); // User-defined object to serialize

    DataContractJsonSerializer serializer = 
      new DataContractJsonSerializer(typeof(SaveData));

    FileStream stream = 
      new FileStream(Application.persistentDataPath, FileMode.CreateNew);

    serializer.WriteObject(stream, data);
    stream.Dispose();
}

I/O 操作

System.IO 命名空间中的一些类型,如 FileStream,在 .NET Standard 的较早版本中不可用。 但是,Unity 提供目录文件FileStream 类型,以便你可以在游戏中使用它们。

或者,你可以使用 Windows.Storage API,这些 API 仅适用于 UWP 应用。 但是,这些 API 限制应用写入到它们的特定存储中,且不允许它自由访问整个文件系统。 请参阅文件、文件夹和库获取详细信息。

一个重要说明是关闭方法仅在 .NET Standard 2.0 及更高版本中可用(但 Unity 提供了扩展方法)。 改用处置

线程

System.Threading 命名空间中的一些类型,如 ThreadPool,在 .NET Standard 的较早版本中不可用。 在这些情况下,你可以改用 Windows.System.Threading 命名空间。

下面介绍在 Unity 游戏中如何使用依赖于平台的编译处理线程,以便为 UWP 和非 UWP 平台做准备:

private void UsingThreads()
{
#if NETFX_CORE
    Windows.System.Threading.ThreadPool.RunAsync(workItem => SomeMethod());
#else
    System.Threading.ThreadPool.QueueUserWorkItem(workItem => SomeMethod());
#endif
}

安全性

在为 UWP 生成 Unity 游戏时,有些 System.Security.* 命名空间(如 System.Security.Cryptography.X509Certificates)不可用。 在这些情况下,请使用 Windows.Security.* API,其具有许多相同的功能。

下面的示例只是从具有指定名称的证书存储获取证书:

private async void GetCertificatesAsync(string certStoreName)
    {
#if NETFX_CORE
        IReadOnlyList<Certificate> certs = await CertificateStores.FindAllAsync();
        IEnumerable<Certificate> myCerts = 
            certs.Where((certificate) => certificate.StoreName == certStoreName);
#else
        X509Store store = new X509Store(certStoreName, StoreLocation.CurrentUser);
        store.Open(OpenFlags.OpenExistingOnly);
        X509Certificate2Collection certs = store.Certificates;
#endif
    }

有关使用 WinRT 安全 API 的详细信息,请参阅安全性

网络

有些 System.Net.* 命名空间(如 System.Net.Mail)在为 UWP 生成 Unity 游戏时同样不可用。 对于这些 API 的大多数,请使用相应的 Windows.Networking.* 和Windows.Web.* WinRT API 来获取类似的功能。 有关详细信息,请参阅网络和 Web 服务

对于 System.Net.Mail,请使用 Windows.ApplicationModel.Email 命名空间。 有关详细信息,请参阅发送电子邮件

另请参阅