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

程序集定义

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

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

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

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

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

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

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

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

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

另请参阅:

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

定义程序集

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

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

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

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

引用和依赖项

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

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

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

默认引用

默认情况下,预定义程序集会引用所有其他程序集,包括使用程序集定义创建的程序集 (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

Defining symbols based on Unity and project package versions

If you need to compile different code in an assembly according to whether a project uses specific versions of Unity or of a package, you can add entries to the Version Defines list. This list specifies rules for when a symbol should be defined. For version numbers, you can specify a logical expression that evaluates to a specific version or a range of versions.

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

  1. Select the Assembly Definition asset for the assembly to view its properties in the Inspector.

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

  3. 设置属性:

    • Resource: choose Unity or the package or module that must be installed in order for this symbol to be defined
    • __Define__:符号名称
    • __Expression__:计算结果为特定版本或版本范围的表达式。有关规则,请参阅[版本定义表达式]。

    The Expression outcome shows which versions the expression evaluates to. If the outcome displays, Invalid, then expression syntax is incorrect.

    The following example defines the symbol, USE_TIMELINE_1_3, if the project uses Timeline 1.3 and defines, USE_NEW_APIS, if the project is opened in Unity 2021.2.0a7, or later:

  4. 单击 Apply

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

Note that you can use the symbols defined using the Version Defines list as Define Constraints. Thus you could specify that an assembly should only be used when specific versions of a given package are also installed in the project.

版本定义表达式

You can use expressions to specify an exact version or a range of versions. A Version Define expression uses mathematical range notation.

A square bracket, “[]” designates that the range includes the endpoint:

[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

Note: No spaces are allowed in an expression. No wildcard characters are supported.

Unity version numbers

Current versions of Unity (and all versions that support Assembly Definitions) use a version designator with three parts: MAJOR.MINOR.REVISION, for example, 2017.4.25f1, 2018.4.29f1, and 2019.4.7f1.

  • The MAJOR version is the target release year, such as 2017 or 2021.
  • The MINOR version is the target release quarter, such as 1, 2, 3, or 4.
  • The REVISION designator has three parts of its own, with the format: RRzNN, where:
    • RR is a one or two digit revision number
    • z is a letter designating the release type:
      • a = alpha release
      • b = beta release
      • f = a normal public release
      • c = China release version (equivalent to f)
      • p = patch release
      • x = experimental release
    • NN is one or two digit incremental number

Release type designators are compared as follows:

a < b < f = c < p < x

In other words, an alpha release is considered earlier than a beta, which is earlier than a normal (f) or China (c) release. A patch release is always later than a normal or China release with the same revision number and an experimental release is later than any other release type. Note that experimental releases do not use an incremental number at the end.

Unity version numbers are allowed to have a suffix after the REVISION component, such as 2019.3.0f11-Sunflower. Any suffixes are ignored for the purpose of comparing versions.

As an example, the following expression includes any 2017 or 2018 version of Unity, but not any version in 2019 or later:

[2017,2019)

Package and module version numbers

Package and module version designators have four parts, following the Semantic Versioning format: MAJOR.MINOR.PATCH-LABEL. The first three parts are always numbers, but the label is a string. Unity packages in preview use the string, preview or preview.n, where n > 0. See Package Versioning for more information about package version numbers.

For example, the following expression includes all versions of a package with MAJOR.MINOR versions between 3.2 and 6.1 (inclusive):

[3.2,6.1]

查找脚本所属的程序集

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

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

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

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

特殊文件夹

Unity 对具有某些特殊名称的文件夹中的脚本进行处理的方式不同于其他文件夹中的脚本。但是,在这些文件夹之一中或上级的一个文件夹中创建程序集定义资源时,该文件夹将失去特殊处理。在使用 Editor 文件夹时可能会注意到该变化,这些文件夹可能分散在整个项目中(取决于代码的组织方式以及所用的 Asset Store 包)。

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);
        }
    }
}
平台相关的编译
程序集定义 (Assembly Definition) 属性