Unity 和 UWP 中缺少的 .NET APIMissing .NET APIs in Unity and UWP

在使用 .NET 生成 UWP 游戏时,你可能会发现你在 Unity 编辑器中可能使用的或用于独立电脑游戏的一些 API 对于 UWP 不存在。When building a UWP game using .NET, you may find that some APIs that you might use in the Unity editor or for a standalone PC game are not present for UWP. 这是因为适用于 UWP 应用的 .NET 包括每个命名空间的完整 .NET Framework 中提供的类型子集。That's because .NET for UWP apps includes a subset of the types provided in the full .NET Framework for each namespace.

此外,某些游戏引擎使用与适用于 UWP 的 .NET 不完全兼容的不同 .NET 风格,如 Unity 的 Mono。Additionally, some game engines use different flavors of .NET that aren't fully compatible with .NET for UWP, such as Unity's Mono. 因此在你编写游戏时,编辑器中可能完全正常,但当你针对 UWP 进行构建时,你可能会看到如下错误:类型或命名空间“格式化程序”在命名空间“System.Runtime.Serialization”中不存在(你是否缺少程序集引用?)So when you're writing your game, everything might work fine in the editor, but when you go to build for UWP, you might get errors like this: The type or namespace 'Formatters' does not exist in the namespace 'System.Runtime.Serialization' (are you missing an assembly reference?)

幸运的是,Unity 提供这些缺失的 API 的一部分作为扩展方法和替代类型,在通用 Windows 平台:在 .NET 脚本后端缺少的 .NET 类型对此进行了描述。Fortunately, Unity provides some of these missing APIs as extension methods and replacement types, which are described in Universal Windows Platform: Missing .NET Types on .NET Scripting Backend. 但是,如果你需要的功能不在此处,则 适用于 Windows 8. x 应用的 .net 概述 介绍了将代码转换为对 Windows 运行时 Api 使用 WinRT 或 .net 的方法。However, if the functionality you need is not here, .NET for Windows 8.x apps overview discusses ways you can convert your code to use WinRT or .NET for Windows Runtime APIs. (它讨论的是 Windows 8,但也适用于 Windows 10 UWP 应用。)(It discusses Windows 8, but is applicable to Windows 10 UWP apps as well.)

.NET Standard.NET Standard

要了解一些 API 为什么无法工作,须了解不同的 .NET 风格以及 UWP 如何实现 .NET。To understand why some APIs might not be working, it's important to understand the different .NET flavors and how UWP implements .NET. .NET Standard 是 .NET API 的正式规范,适用于各个平台,并统一不同的 .NET 风格。The .NET Standard is a formal specification of .NET APIs that is meant to be cross-platform, and unify the different .NET flavors. .NET 的每个实现都支持特定版本的 .NET Standard。Each implementation of .NET supports a certain version of the .NET Standard. 你可以参阅 .NET 实现支持中的标准和实现表。You can see a table of standards and implementations at .NET implementation support.

UWP SDK 的每个版本都符合不同级别的 .NET Standard。Each version of the UWP SDK conforms to a different level of .NET Standard. 例如,16299 SDK(秋季创作者更新)支持 .NET Standard 2.0。For example, the 16299 SDK (the Fall Creators Update) supports .NET Standard 2.0.

如果你要了解特定的 .NET API 在你的目标 UWP 版本中是否受支持,可以查看 .NET Standard API 参考并选择该 UWP 版本支持的 .NET Standard 版本。If you want to know if a certain .NET API is supported in the UWP version that you're targeting, you can check the .NET Standard API Reference and select the version of the .NET Standard that's supported by that version of UWP.

脚本后端配置Scripting backend configuration

如果你在针对 UWP 进行生成时遇到麻烦,首先应该检查播放器设置文件 > 生成设置,并依次选择通用 Windows 平台播放器设置)。The first thing you should do if you're having trouble building for UWP is check the Player Settings (File > Build Settings, select Universal Windows Platform, and then Player Settings). 其他设置 > 配置下,前三个下拉列表(脚本运行时版本脚本后端API 兼容性级别)都是需要考虑的重要设置。Under Other Settings > Configuration, the first three dropdowns (Scripting Runtime Version, Scripting Backend, and Api Compatibility Level) are all important settings to consider.

Unity 脚本后端使用脚本运行时版本来允许你获取你选择的 .NET Framework 支持的(大致)等效版本。The Scripting Runtime Version is what the Unity scripting backend uses which allows you to get the (roughly) equivalent version of .NET Framework support that you choose. 但请记住,该版本的 .NET Framework 中的所有 API 并非全部受支持,仅在你的 UWP 的目标 .NET Standard 版本中的 API 受支持。However, keep in mind that not all APIs in that version of the .NET Framework will be supported, only those in the version of .NET Standard that your UWP is targeting.

在新发布的 .NET 中通常会将更多 API 添加到 .NET Standard 中,这可能允许你在独立版和 UWP 中使用相同代码。Often with new .NET releases, more APIs are added to .NET Standard which might allow you to use the same code across standalone and UWP. 例如,.NET Standard 2.0 中引入了 System.Runtime.Serialization.Json 命名空间。For example, the System.Runtime.Serialization.Json namespace was introduced in .NET Standard 2.0. 如果你将脚本运行时版本设置为 .NET 3.5 等效项(针对较早版本的 .NET Standard),你在尝试使用 API 时会遇到错误;将其切换到 .NET 4.6 等效项(支持 .NET Standard 2.0),该 API 将正常运行。If you set the Scripting Runtime Version to .NET 3.5 Equivalent (which targets an earlier version of the .NET Standard), you will get an error when trying to use the API; switch it to .NET 4.6 Equivalent (which supports .NET Standard 2.0), and the API will work.

脚本后端可以为 .NETIL2CPPThe Scripting Backend can be .NET or IL2CPP. 在本主题中,我们假设你已选择 .NET,因为在此处讨论的问题是在这种情况下发生的。For this topic, we assume you have chosen .NET, since that's where the problems discussed here arise. 请参阅脚本后端获取详细信息。See Scripting Backends for more information.

最后,你应将 API 兼容性级别设置为你要运行游戏的 .NET 版本。Finally, you should set the Api Compatibility Level to the version of .NET that you want your game to run on. 它应该匹配脚本运行时版本This should match the Scripting Runtime Version.

一般情况下,对于脚本运行时版本API 兼容性级别,你应该选择可用的最新版本,以便提高与 .NET Framework 的兼容性,你也可以使用更多的 .NET API。In general, for Scripting Runtime Version and Api Compatibility Level, you should select the latest version available so as to have more compatibility with the .NET Framework, and thus allow you to use more .NET APIs.

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

依赖于平台的编译Platform-dependent compilation

如果你正在为多个平台(包括 UWP)生成 Unity 游戏,你需要使用依赖于平台的编译,以确保仅当作为 UWP 生成游戏时运行适用于 UWP 的代码。If you're building your Unity game for multiple platforms, including UWP, you'll want to use platform-dependent compilation to make sure that code intended for UWP is only run when the game is built as a UWP. 这样一来,你可以对独立桌面和其他平台使用完整的 .NET Framework,对 UWP 使用 WinRT API,不会发生生成错误。This way, you can use the full .NET Framework for standalone desktop and other platforms, and WinRT APIs for UWP, without getting build errors.

使用以下指令,以便仅在作为 UWP 应用运行时编译代码:Use the following directives to only compile code when running as a UWP app:

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

备注

NETFX_CORE 仅用于检查是否正在针对 .NET 脚本编写后端编译 c # 代码。NETFX_CORE is only meant to check if you're compiling C# code against the .NET scripting backend. 如果你使用的是不同的脚本后端(例如 IL2CPP),请 ENABLE_WINMD_SUPPORT 改用。If you're using a different scripting backend, such as IL2CPP, use ENABLE_WINMD_SUPPORT instead.

若要获取依赖于平台的编译指令的完整列表,请参阅依赖于平台的编译For the full list of platform-dependent compilation directives, see Platform dependent compilation.

常见问题和解决方法Common issues and workarounds

以下方案描述当 UWP 子集中缺少 .NET API 时可能出现的常见问题以及解决方法。The following scenarios describe common issues that might arise where .NET APIs are missing from the UWP subset, and ways to get around them.

使用 BinaryFormatter 进行数据序列化Data serialization using BinaryFormatter

游戏经常序列化保存数据,使玩家无法轻松操控它。It is common for games to serialize save data so that players can't easily manipulate it. 但是,将对象序列化为二进制的 BinaryFormatter 在 .NET Standard 的较早版本(2.0 以前)中不可用。However, BinaryFormatter, which serializes an object into binary, is not available in earlier versions of the .NET Standard (prior to 2.0). 请考虑改用 XmlSerializerDataContractJsonSerializerConsider using XmlSerializer or DataContractJsonSerializer instead.

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 操作I/O operations

System.IO 命名空间中的一些类型,如 FileStream,在 .NET Standard 的较早版本中不可用。Some types in the System.IO namespace, such as FileStream, are not available in earlier versions of the .NET Standard. 但是,Unity 提供目录文件FileStream 类型,以便你可以在游戏中使用它们。However, Unity does provide the Directory, File, and FileStream types so you can use them in your game.

或者,你可以使用 Windows.Storage API,这些 API 仅适用于 UWP 应用。Alternatively, you can use the Windows.Storage APIs, which are only available to UWP apps. 但是,这些 API 限制应用写入到它们的特定存储中,且不允许它自由访问整个文件系统。However, these APIs restrict the app to writing to their specific storage, and do not give it free access to the entire file system. 请参阅文件、文件夹和库获取详细信息。See Files, folders, and libraries for more information.

一个重要说明是关闭方法仅在 .NET Standard 2.0 及更高版本中可用(但 Unity 提供了扩展方法)。One important note is that the Close method is only available in .NET Standard 2.0 and later (though Unity provides an extension method). 改用处置Use Dispose instead.

线程Threading

System.Threading 命名空间中的一些类型,如 ThreadPool,在 .NET Standard 的较早版本中不可用。Some types in the System.Threading namespaces, such as ThreadPool, are not available in earlier versions of the .NET Standard. 在这些情况下,你可以改用 Windows.System.Threading 命名空间。In these cases, you can use the Windows.System.Threading namespace instead.

下面介绍在 Unity 游戏中如何使用依赖于平台的编译处理线程,以便为 UWP 和非 UWP 平台做准备:Here's how you could handle threading in a Unity game, using platform-dependent compilation to prepare for both UWP and non-UWP platforms:

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

安全Security

在为 UWP 生成 Unity 游戏时,有些 System.Security.* 命名空间(如 System.Security.Cryptography.X509Certificates)不可用。Some of the System.Security.* namespaces, such as System.Security.Cryptography.X509Certificates, are not available when you build a Unity game for UWP. 在这些情况下,请使用 Windows.Security.* API,其具有许多相同的功能。In these cases, use the Windows.Security.* APIs, which cover much of the same functionality.

下面的示例只是从具有指定名称的证书存储获取证书:The following example simply gets the certificates from a certificate store with the given name:

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 的详细信息,请参阅安全性See Security for more information about using the WinRT security APIs.

网络Networking

有些 System.Net.* 命名空间(如 System.Net.Mail)在为 UWP 生成 Unity 游戏时同样不可用。Some of the System.Net.* namespaces, such as System.Net.Mail, are also not available when building a Unity game for UWP. 对于这些 API 的大多数,请使用相应的 Windows.Networking.* 和Windows.Web.* WinRT API 来获取类似的功能。For most of these APIs, use the corresponding Windows.Networking.* and Windows.Web.* WinRT APIs to get similar functionality. 有关详细信息,请参阅网络和 Web 服务See Networking and web services for more information.

对于 System.Net.Mail,请使用 Windows.ApplicationModel.Email 命名空间。In the case of System.Net.Mail, use the Windows.ApplicationModel.Email namespace. 有关详细信息,请参阅发送电子邮件See Send email for more information.

另请参阅See also