Version: 2020.1
平台相关的编译
程序集定义 (Assembly Definition) 属性

程序集定义

程序集定义和程序集引用是可创建用于将脚本组织为程序集的资源。

程序集是 C# 代码库,其中包含由脚本定义的已编译类和结构并且还定义了对其他程序集的引用。有关 C# 中的程序集的一般信息,请参阅 [.NET 中的程序集]。

默认情况下,Unity 几乎将所有游戏脚本都编译到预定义 程序集 Assembly-CSharp.dll 中。(Unity 还会创建[一些较小的专用预定义程序集]。)

这种安排对于小型项目而言可以接受,但是在向项目添加更多代码时会有一些缺点:

  • 每次更改一个脚本时,Unity 都必须重新编译所有其他脚本,从而增加迭代代码更改的整体编译时间。
  • 任何脚本都可以直接访问任何其他脚本中定义的类型,这样可能更加难以重构和改进代码。
  • 所有脚本都针对所有平台进行编译。

通过定义程序集,可以组织代码以促进模块化和可重用性。为项目定义的程序集中的脚本不再添加到默认程序集中,并且只能访问指定的其他程序集中的脚本。

上图演示了如何将项目中的代码拆分为多个程序集。因为 Main 引用 Stuff 并且不进行反向引用,所以知道对 Main 中的代码进行的任何更改都不会影响 Stuff 中的代码。同样,因为 Library 不依赖于任何其他程序集,所以可以更轻松地在其他项目中重用 Library 中的代码。

本部分将讨论如何创建和设置程序集定义和程序集引用资源以便为项目定义程序集:

  • [定义程序集]
  • [引用和依赖项]
  • [创建程序集定义资源]
  • [创建程序集定义引用资源]
  • [创建特定于平台的程序集]
  • [为编辑器代码创建程序集]
  • [创建测试程序集]
  • [引用另一个程序集]
  • [引用预编译的插件程序集]
  • [有条件地构建程序集]
  • [基于项目包定义符号]
  • [查找脚本所属的程序集]
  • [特殊文件夹]
  • [在生成脚本中获取程序集信息]

另请参阅:

  • [程序集定义属性]
  • [程序集定义引用属性]
  • [程序集定义文件格式]

定义程序集

要将项目代码组织成程序集,请为每个所需程序集创建一个文件夹,并将应属于每个程序集的脚本移动到相关文件夹中。然后[创建程序集定义资源]以指定程序集属性。

Unity 会获取包含程序集定义资源的文件夹中的所有脚本,并使用该资源定义的名称和其他设置将它们编译为程序集。Unity 还包含同一程序集中的任何子文件夹中的脚本,除非子文件夹具有自己的程序集定义或程序集引用资源。

要包含来自现有程序集中一个非子文件夹的脚本,请在该非子文件夹中创建一个程序集引用资源,并将它设置为引用定义目标程序集的程序集定义资源。例如,可以将来自项目中所有 Editor 文件夹的脚本合并到它们自己的程序集中,无论这些文件夹位于何处。

Unity 会按照由其依赖项确定的顺序编译程序集;无法指定进行编译的顺序。

引用和依赖项

当一种类型(如类或结构)使用另一种类型时,第一种类型依赖于 第二种类型。当 Unity 编译脚本时,它还必须可以访问该脚本所依赖的任何类型或其他代码。同样,当已编译的代码运行时,它必须可以访问其依赖项的已编译版本。如果两种类型处于不同的程序集中,则包含依赖类型的程序集必须声明对包含它所依赖类型的程序集的引用

可以使用程序集定义的选项控制项目中使用的程序集之间的引用。程序集定义设置包括:

  • [Auto Referenced] – 预定义程序集是否引用相应程序集
  • [Assembly Definition References] – 对使用程序集定义创建的其他项目程序集的引用
  • [Override References] + [Assembly References] – 对预编译(插件)程序集的引用
  • [No Engine References] – 对 UnityEngine 程序集的引用

__注意:__使用程序集定义创建的程序集中的类不能使用预定义程序集中定义的类型。

默认引用

默认情况下,预定义程序集会引用所有其他程序集,包括使用程序集定义创建的程序集 (1) 以及作为插件添加到项目中的预编译程序集 (2)。此外,使用程序集定义资源创建的程序集会自动引用所有预编译程序集 (3):

在默认设置中,预定义程序集中的类可以使用项目中任何其他程序集定义的所有类型。同样,使用程序集定义资源创建的程序集可以使用在任何预编译(插件)程序集中定义的所有类型。

可以通过在程序集定义资源的 Inspector 中关闭 [Auto Referenced 选项]来防止预定义程序集引用某个程序集。关闭自动引用意味着在更改程序集中的代码时不会重新编译预定义程序集,但也意味着预定义程序集无法直接使用此程序集中的代码。请参阅[程序集定义 (Assembly Definition) 属性]。

同样,可以通过在插件资源的 [Plugin Inspector] 中关闭 [Auto Referenced 属性]来防止自动引用插件程序集。这会影响预定义程序集以及使用程序集定义创建的程序集。请参阅 [Plugin Inspector] 以了解更多信息。

关闭__插件__的 Auto Referenced 时,可以在 Inspector 中为程序集定义资源显式引用它。请启用该资源的 [Override References] 选项,然后添加对__插件__的引用。请参阅[程序集定义 (Assembly Definition) 属性]。

__注意:__无法声明预编译程序集的显式引用。预定义程序集只能使用自动引用的程序集中的代码。

循环引用

当一个程序集引用第二个程序集,而第二个程序集又引用第一个程序集时,便存在循环程序集引用。程序集之间的这类循环引用是不允许的,会报告为错误并显示消息“Assembly with cyclic references detected”。

通常,程序集之间的这类循环引用是由于程序集中定义的类中的循环引用而发生的。虽然同一程序集中的类之间的循环引用在技术上没有什么无效之处,但不允许不同程序集中的类之间进行循环引用。如果遇到循环引用错误,则必须重构代码以移除循环引用或将相互引用的类置于同一程序集中。

创建程序集定义资源

要创建程序集定义资源,请执行以下操作:

  1. Project 窗口中,找到包含要包括在程序集中的脚本的文件夹。
  2. 在该文件夹中创建程序集定义资源(菜单:__Assets__ > Create > __Assembly Definition__)。
  3. 为资源分配名称。默认情况下,程序集文件使用分配给资源的名称,不过可以在 Inspector 窗口中更改名称。

Unity 会重新编译项目中的脚本以创建新程序集。完成后,便可以为新程序集定义更改设置。

包含程序集定义的文件夹中的脚本(包括任何子文件夹中的脚本(除非这些文件夹包含其自己的程序集定义或引用资源))会编译到新程序集中并从它们以前的程序集中移除。

创建程序集定义引用资源

要创建程序集定义引用资源,请执行以下操作:

  1. Project 窗口中,找到包含要包括在引用程序集中的脚本的文件夹。

  2. 在该文件夹中创建程序集引用资源(菜单:__Assets__ > Create > __Assembly Definition Reference__)。

  3. 为资源分配名称。

    Unity 会重新编译项目中的脚本以创建新程序集。完成后,便可以为新程序集定义引用更改设置。

  4. 选择新的程序集定义引用资源以在 Inspector 中查看其属性。

  5. 设置 Assembly Definition 属性以引用目标程序集定义资源。

  6. 单击 Apply

包含程序集定义引用资源的文件夹中的脚本(包括任何子文件夹中的脚本(除非这些文件夹包含其自己的程序集定义或引用资源))会编译到引用程序集中并从它们以前的程序集中移除。

创建特定于平台的程序集

要为特定平台创建程序集,请执行以下操作:

  1. [创建程序集定义资源]。

  2. 选择新的程序集定义引用资源以在 Inspector 中查看其属性。

  3. 选中 Any Platform 选项并选择要排除的特定平台。或者,可以取消选中 Any Platform 并选择要包含的特定平台。

  4. 单击 Apply

为平台构建项目时,会根据选定平台包含(或排除)程序集。

为编辑器代码创建程序集

通过编辑器程序集可以将编辑器脚本置于项目中的任何位置,而不仅仅是置于名为 Editor 的顶层文件夹中。

要在项目中创建包含编辑器代码的程序集,请执行以下操作:

  1. 在包含编辑器脚本的文件夹中[创建特定于平台的程序集]。
  2. 仅包含编辑器平台。
  3. 如果有包含编辑器脚本的其他文件夹,则在这些文件夹中[创建程序集定义引用资源]并将它们设置为引用此程序集定义。

创建测试程序集

通过测试程序集可以编写测试并使用 Unity TestRunner 运行它们,同时还使测试代码与应用程序附带的代码分开。Unity 提供 TestRunner 作为 [Test Framework 包]的一部分。请参阅 [Test Framework 文档]以了解有关安装 Test Framework 包和创建测试程序集的说明。

引用另一个程序集

要使用属于另一个程序集一部分的 C# 类型和函数,必须在程序集定义资源中创建对该程序集的引用。

要创建程序集引用,请执行以下操作:

  1. 选择需要引用的程序集的程序集定义以在 Inspector 中查看其属性。

  2. Assembly Definition References 部分中,单击 + 按钮以添加新引用。

  3. 将程序集定义资源分配给引用列表中新创建的字段。

通过启用 Use GUIDs 选项可以更改引用程序集定义资源的文件名,而无需更新其他程序集定义中的引用以反映新名称。(请注意,如果删除了资源文件的元数据文件,或者将文件移到 Unity 编辑器之外,而没有同时随它们移动元数据文件,则必须重置 GUID。)

引用预编译的插件程序集

默认情况下,项目中使用程序集定义创建的所有程序集都会自动引用所有预编译程序集。这些自动引用意味着在更新任何一个预编译程序集时,Unity 都必须重新编译所有程序集,即使未使用程序集中的代码也是如此。要避免这种额外开销,可以覆盖自动引用并指定仅引用程序集实际使用的预编译库:

  1. 选择需要引用的程序集的程序集定义以在 Inspector 中查看其属性。

  2. General 部分中,启用 Override References 选项。

    选中 Override References 后,__Inspector__ 的 Assembly References 部分会成为可用状态。

  3. Assembly References 部分中,单击 + 按钮以添加新引用。

  4. 使用空字段中的下拉列表分配对预编译程序集的引用。该列表会显示项目中适用于当前在项目 [Build Settings] 中设置的平台的所有预编译程序集。(可在 [Plugin Inspector] 中为预编译程序集设置平台兼容性。)

  5. 单击 Apply

  6. 为构建项目时针对的每个平台重复操作。

有条件地包含一个程序集

可以使用预处理器符号控制程序集是否进行了编译并包含在游戏或应用程序的构建中(包括编辑器中的运行模式)。可以在程序集定义选项中通过 Define Constraints 列表指定必须为要使用的程序集定义的符号:

  1. 选择程序集的程序集定义以在 Inspector 中查看其属性。

  2. Define Constraints 部分中,单击 + 按钮以将新符号添加到约束列表中。

  3. 输入符号名称。

    可以通过在名称前放置感叹号来“否定”符号。例如,约束 !UNITY_WEBGL 会在未定义 UNITY_WEBGL 时包含程序集。

  4. 单击 Apply

可以使用以下符号作为约束:

  • [Scripting Define Symbols] 设置中定义的符号,可以在 Project SettingsPlayer 部分中找到这些符号。请注意,__Scripting Define Symbols__ 适用于当前在项目 [Build Settings] 中设置的平台。要为多个平台定义一个符号,必须切换到每个平台并单独修改 Scripting Define Symbols 字段。
  • Unity 定义的符号。请参阅[依赖于平台的编译]。
  • 使用程序集定义资源的 [Version Defines] 部分定义的符号。

在确定是否满足约束时,不会考虑脚本中定义的符号。

有关其他信息,请参阅 [Define Constraints]。

基于项目包定义符号

如果需要根据项目是使用特定包还是包的各个版本,在程序集中编译不同代码,则可以向 Version Defines 列表添加条目。此列表为何时定义符号指定规则。对于版本号,可以指定计算结果为特定版本或版本范围的逻辑表达式。

要有条件地定义符号,请执行以下操作:

  1. 选择程序集的程序集定义以在 Inspector 中查看其属性。

  2. Version Defines 部分中,单击 + 按钮以将条目添加到列表中。

  3. 设置属性:

    Resource__:选择必须按顺序安装才能定义此符号的包或模块 * Define__:符号名称 __Expression__:计算结果为特定版本或版本范围的表达式。有关规则,请参阅[版本定义表达式]。

    Expression outcome 显示表达式计算得出的版本。

    下面的示例定义符号 USE_TIMELINE(如果项目使用 Timeline 1.3.0 或更高版本):

  4. 单击 Apply

在程序集定义中定义的符号仅在为该定义创建的程序集中的脚本范围内。

请注意,可以使用通过 Version Defines 列表定义为 Define Constraints 的符号。因此,可以指定仅当在项目中安装了给定包的特定版本时才使用程序集。

版本定义表达式

可以使用表达式指定确切版本或版本范围。__版本定义__表达式使用数学范围表示法。方括号“[]”指定范围包括端点:

[1.3,3.4.1] 计算为 1.3.0 <= x <= 3.4.1

圆括号“()”指定范围不包括端点:

(1.3.0,3.4) 计算为 1.3.0 < x < 3.4.0

可以在单一表达式中混合两种范围类型:

[1.1,3.4) 计算为 1.1.0 <= x < 3.4.0

(0.2.4,5.6.2-preview.2] 计算为 0.2.4 < x <= 5.6.2.-preview.2

可以在方括号中使用单一版本指示符来指定确切版本:

[2.4.5] 计算为 x = 2.4.5

作为快捷方式,可以输入不带范围括号的单一版本指示表达式包含该版本或更高版本:

2.1.0-preview.7 计算为 x >= 2.1.0-preview.7

版本指示符有四个部分,遵循[语义版本控制]格式:MAJOR.MINOR.PATCH-LABEL。前三个部分始终是数字,但标签是字符串。预览版的 Unity 包使用字符串 previewpreview.n,其中 n > 0。必须至少在表达式中使用版本的 major 和 minor 组成部分。

__注意:__表达式中不允许存在空格。

查找脚本所属的程序集

要确定一个 C# 脚本编译到哪个程序集中,请执行以下操作:

  1. 在 Unity Project 窗口中选择 C# 脚本文件以在 Inspector 窗口中查看其属性。

  2. 程序集文件名和程序集定义(如果存在)会显示在 InspectorAssembly Information 部分中。

在此示例中,选定脚本会编译到库文件 Unity.Timeline.Editor.dll 中,该文件由 Unity.Timeline.Editor 程序集定义资源进行定义。

特殊文件夹

Unity treats scripts in folders with certain, special names differently than scripts in other folders. However, one of these folders loses its special treatment when you create an Assembly Definition asset inside it or in a folder above it. You might notice this change when you use Editor folders, which might be scattered throughout your Project (depending on how you organize your code and on the Asset packages you use).

Unity 通常都会将名为 Editor 的文件夹中的所有脚本编译到预定义的 Assembly-CSharp-Editor 程序集中(无论这些脚本位于何处)。但是,如果在下级有一个 Editor 文件夹的文件夹中创建程序集定义资源,则 Unity 不再将这些编辑器脚本放入预定义编辑器程序集中。实际上,这些脚本会进入通过程序集定义创建的新程序集中(脚本可能不属于该程序集)。要管理 Editor 文件夹,可以在每个 Editor 文件夹中创建程序集定义或引用资源,以将这些脚本放置在一个或多个编辑器程序集中。请参阅[为编辑器代码创建程序集]。

设置程序集属性

可以使用程序集属性 (Attribute) 为程序集设置元数据属性 (Property)。按照惯例,程序集属性语句通常置于名为 AssemblyInfo.cs 的文件中。

例如,以下程序集属性指定了一些 [.NET 程序集元数据值]、一个 [InternalsVisibleTo] 属性(对于测试可能十分有用)以及 Unity 定义的 [Preserve 属性](影响构建项目时如何从程序集中移除未使用的代码):

[assembly: System.Reflection.AssemblyCompany("Bee Corp.")]
[assembly: System.Reflection.AssemblyTitle("Bee's Assembly")]
[assembly: System.Reflection.AssemblyCopyright("Copyright 2020.")]
[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("UnitTestAssembly")]
[assembly: UnityEngine.Scripting.Preserve]

在生成脚本中获取程序集信息

使用 UnityEditor.Compilation 命名空间中的 CompilationPipeline 类可检索 Unity 为项目构建的所有程序集(包括基于程序集定义资源创建的程序集)的相关信息。

例如,以下脚本使用 CompilationPipeline 类列出项目中的所有当前播放器程序集:

using UnityEditor;
using UnityEditor.Compilation;
public static class AssemblyLister
{
    [MenuItem("Tools/List Player Assemblies in Console")]
    public static void PrintAssemblyNames()
    {
        UnityEngine.Debug.Log("== Player Assemblies ==");
        Assembly[] playerAssemblies =
            CompilationPipeline.GetAssemblies(AssembliesType.Player);

        foreach (var assembly in playerAssemblies)
        {
            UnityEngine.Debug.Log(assembly.name);
        }
    }
}

[.NET 程序集元数据值]:https://docs.microsoft.com/en-us/dotnet/standard/assembly/set-attributes [.NET 中的程序集]:https://docs.microsoft.com/en-us/dotnet/standard/assembly/ [程序集定义文件格式]:AssemblyDefinitionFileFormat.html [程序集定义引用属性]:class-AssemblyDefinitionImporter.html#assembly-definition-reference-properties [程序集定义引用]:class-AssemblyDefinitionReferenceImporter.html [程序集定义属性]:class-AssemblyDefinitionImporter.html [程序集引用]:#reference-another-assembly [Auto Referenced]:class-AssemblyDefinitionImporter.html#general [Auto Referenced 选项]:class-AssemblyDefinitionImporter.html#general [Auto Referenced 属性]:PluginInspector.html [Build Settings]:BuildSettings.html [有条件地构建程序集]:#conditional-assembly [创建特定于平台的程序集]:#create-platform-specific [创建特定于平台的程序集]:#create-platform-specific [创建测试程序集]:#create-test-assembly [创建程序集定义引用资源]:#create-asmref [创建程序集定义资源]:#create-asmdef [为编辑器代码创建程序集]:#create-editor-assembly [Define Constraints]:#define-constraints [定义程序集]:#defining-assemblies [基于项目包定义符号]:#define-symbols [查找脚本所属的程序集]:#find-assembly [在生成脚本中获取程序集信息]:#get-assembly-info [InternalsVisibleTo]:https://docs.microsoft.com/en-us/dotnet/api/system.runtime.compilerservices.internalsvisibletoattribute?view=netcore–2.0 [No Engine References]:class-AssemblyDefinitionImporter.html#general [Override References]:class-AssemblyDefinitionImporter.html#general [依赖于平台的编译]:PlatformDependentCompilation.html [Plugin Inspector]:PluginInspector.html [Preserve 属性]:ManagedCodeStripping.html#PreserveAttribute [引用和依赖项]:#reference-and-assemblies [引用预编译的插件程序集]:#reference-precompiled-assembly [引用另一个程序集]:#reference-another-assembly [Scripting Define Symbols]:class-PlayerSettingsStandalone.html#Configuration [语义版本控制]:https://semver.org/ [特殊文件夹]:#special-folders [Test Framework 文档]:https://docs.unity3d.com/Packages/com.unity.test-framework@latest?subfolder=/manual/workflow-create-test-assembly.html [Test Framework 包]:https://docs.unity3d.com/Manual/com.unity.test-framework.html [版本定义表达式]:#version-define-expressions [Version Defines]:#define-symbols [创建程序集定义资源]:#create-asmdef [创建程序集定义引用资源]:#create-asmref [创建程序集定义资源]:#create-asmdef [一些较小的专用预定义程序集]:ScriptCompileOrderFolders.html

平台相关的编译
程序集定义 (Assembly Definition) 属性