比较属性和项

MSBuild 属性和项都用于将信息传递给任务、评估条件和存储可在整个项目文件中引用的值。

  • 属性是名称值对。 有关详细信息,请参阅 MSBuild 属性

  • 项是通常表示文件的对象。 项对象可以具有关联的元数据集合。 元数据是名称值对。 有关详细信息,请参阅

标量和向量

由于 MSBuild 属性是只有一个字符串值的名称/值对,因此它们通常被描述为 标量。 由于 MSBuild 项类型是项列表,因此它们通常描述为 向量。 但是,在实践中,属性可以表示多个值,项类型可以有零个或一个项。

目标依赖项注入

若要查看属性如何表示多个值,请考虑将目标添加到要生成的目标列表的常见使用模式。 此列表通常由属性值表示,目标名称用分号分隔。

<PropertyGroup>
    <BuildDependsOn>
        BeforeBuild;
        CoreBuild;
        AfterBuild
    </BuildDependsOn>
</PropertyGroup>

BuildDependsOn 属性通常用作目标 DependsOnTargets 属性的参数,有效地将其转换为项列表。 可以重写此属性以添加目标或更改目标执行顺序。 例如,

<PropertyGroup>
    <BuildDependsOn>
        $(BuildDependsOn);
        CustomBuild;
    </BuildDependsOn>
</PropertyGroup>

将 CustomBuild 目标添加到目标列表,提供 BuildDependsOnBeforeBuild;CoreBuild;AfterBuild;CustomBuild

从 MSBuild 4.0 开始,目标依赖项注入已弃用。 请改用 AfterTargets 属性 BeforeTargets 。 有关详细信息,请参阅 目标生成顺序

字符串和项列表之间的转换

MSBuild 根据需要对项类型和字符串值执行转换。 若要查看项列表如何成为字符串值,请考虑将项类型用作 MSBuild 属性的值时会发生什么情况:

<ItemGroup>
    <OutputDir Include="KeyFiles\;Certificates\" />
</ItemGroup>
<PropertyGroup>
    <OutputDirList>@(OutputDir)</OutputDirList>
</PropertyGroup>

Item 类型 OutputDir 具有 Include 值为“KeyFiles\; 的属性Certificates\“. MSBuild 将此字符串分析为两个项目:KeyFiles\ 和 Certificates\。 当项类型 OutputDir 用作 OutputDirList 属性的值时,MSBuild 会将项类型转换为分号分隔字符串“KeyFiles\;Certificates\“.

任务中的属性和项

属性和项用作 MSBuild 任务的输入和输出。 有关详细信息,请参阅 任务

属性作为属性传递给任务。 在该任务中,MSBuild 属性由一个属性类型表示,其值可以转换为字符串和从字符串转换。 支持的属性类型包括bool、、charDateTimeDecimalDoubleint、、和string任何可以处理的类型ChangeType

项作为 ITaskItem 对象传递到任务。 在任务中, ItemSpec 表示项的值并 GetMetadata 检索其元数据。

可将项类型的项列表作为对象数组 ITaskItem 传递。 可以使用属性从目标 Remove 中的项列表中删除项。 由于可以从项列表中删除项,因此项目类型可能具有零项。 如果将项列表传递给任务,则任务中的代码应检查是否存在这种可能性。

属性和项评估顺序

在生成评估阶段,导入的文件会按照生成的显示顺序合并到生成中。 按以下顺序以三个传递定义属性和项:

  • 属性的显示顺序定义和修改。

  • 项定义按照显示的顺序定义和修改。

  • 项的显示顺序定义和修改。

在生成的执行阶段,在目标中定义的属性和项按显示顺序在单个阶段中一起评估。

然而,这不是完整的故事。 定义属性、项定义或项时,将计算其值。 表达式计算器扩展指定值的字符串。 字符串扩展取决于生成阶段。 下面是更详细的属性和项评估顺序:

  • 在生成评估阶段:

    • 属性的显示顺序定义和修改。 执行属性函数。 $(PropertyName)格式的属性值在表达式中展开。 属性值设置为展开的表达式。

    • 项定义按照显示的顺序定义和修改。 属性函数已在表达式中展开。 元数据值设置为展开的表达式。

    • 按项类型显示的顺序定义和修改。 将展开窗体 @(ItemType) 中的项值。 项转换也会展开。 属性函数和值已在表达式中展开。 项列表和元数据值设置为展开的表达式。

  • 在生成执行阶段:

    • 在目标中定义的属性和项按显示顺序一起计算。 执行属性函数,并在表达式中扩展属性值。 项值和项转换也会展开。 属性值、项类型值和元数据值设置为展开的表达式。

计算顺序的细微效果

在生成评估阶段,属性评估在项评估之前。 不过,属性可以具有似乎依赖于项值的值。 请考虑以下脚本。

<ItemGroup>
    <KeyFile Include="KeyFile.cs">
        <Version>1.0.0.3</Version>
    </KeyFile>
</ItemGroup>
<PropertyGroup>
    <KeyFileVersion>@(KeyFile->'%(Version)')</KeyFileVersion>
</PropertyGroup>
<Target Name="AfterBuild">
    <Message Text="KeyFileVersion: $(KeyFileVersion)" />
</Target>

执行消息任务将显示以下消息:

KeyFileVersion: 1.0.0.3

这是因为值 KeyFileVersion 实际上是字符串“@(KeyFile->'%(Version)'”。 首次定义属性时,未展开项和项转换,因此为 KeyFileVersion 属性赋值了未展开的字符串的值。

在生成执行阶段,在处理消息任务时,MSBuild 将展开字符串“@(KeyFile->'%(Version)”,以生成“1.0.0.3”。

请注意,即使属性和项组按顺序反转,也会显示相同的消息。

作为第二个示例,请考虑当属性组和项组位于目标内时会发生什么情况:

<Target Name="AfterBuild">
    <PropertyGroup>
        <KeyFileVersion>@(KeyFile->'%(Version)')</KeyFileVersion>
    </PropertyGroup>
    <ItemGroup>
        <KeyFile Include="KeyFile.cs">
            <Version>1.0.0.3</Version>
        </KeyFile>
    </ItemGroup>
    <Message Text="KeyFileVersion: $(KeyFileVersion)" />
</Target>

消息任务显示以下消息:

KeyFileVersion:

这是因为,在生成的执行阶段,在目标中定义的属性和项组会同时从上到下进行评估。 定义时 KeyFileVersionKeyFile 未知。 因此,项转换扩展到空字符串。

在这种情况下,反转属性和项组的顺序将还原原始消息:

<Target Name="AfterBuild">
    <ItemGroup>
        <KeyFile Include="KeyFile.cs">
            <Version>1.0.0.3</Version>
        </KeyFile>
    </ItemGroup>
    <PropertyGroup>
        <KeyFileVersion>@(KeyFile->'%(Version)')</KeyFileVersion>
    </PropertyGroup>
    <Message Text="KeyFileVersion: $(KeyFileVersion)" />
</Target>

该值 KeyFileVersion 设置为“1.0.0.3”,而不是“@(KeyFile->'%(版本)'”。 消息任务显示以下消息:

KeyFileVersion: 1.0.0.3