比较属性和项
MSBuild 属性和项都用于将信息传递给任务、评估条件,以及存储可在整个项目文件中引用的值。
属性是名称/值对。 详情请参阅 MSBuild 属性。
项是通常表示文件的对象。 项对象具有关联的元数据集合。 元数据是名称/值对。 有关详细信息,请参阅项。
标量和矢量
由于 MSBuild 属性是只有一个字符串值的名称/值对,所以它们通常被描述为标量。 由于 MSBuild 项类型是项列表,所以它们通常被描述为矢量。 但是实际上,属性可以表示多个值,而项类型可以有零个或一个项。
目标依赖关系注入
若要查看属性如何表示多个值,请考虑使用常见使用模式将目标添加到要生成的目标列表。 此列表通常由属性值表示,包含用分号分隔的目标名称。
<PropertyGroup>
<BuildDependsOn>
BeforeBuild;
CoreBuild;
AfterBuild
</BuildDependsOn>
</PropertyGroup>
BuildDependsOn
属性通常用作目标 DependsOnTargets
特性的参数,可有效地将该特性转换为项列表。 可以重写此属性,以添加目标或更改目标执行顺序。 例如,
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
CustomBuild;
</BuildDependsOn>
</PropertyGroup>
将 CustomBuild 目标添加到目标列表中,为 BuildDependsOn
提供值 BeforeBuild;CoreBuild;AfterBuild;CustomBuild
。
从 MSBuild 4.0 开始,弃用目标依赖关系注入。 改用 AfterTargets
和 BeforeTargets
特性。 有关详细信息,请参阅目标生成顺序。
字符串和项列表之间的转换
MSBuild 执行项类型之间的转换,并且需要字符串值。 若要查看项列表如何成为字符串值,请思考项类型用作 MSBuild 属性的值时,会发生什么情况:
<ItemGroup>
<OutputDir Include="KeyFiles\;Certificates\" />
</ItemGroup>
<PropertyGroup>
<OutputDirList>@(OutputDir)</OutputDirList>
</PropertyGroup>
项类型 OutputDir 具有值为“KeyFiles\;Certificates\”的 Include
属性。 MSBuild 将此字符串分析为两个项:KeyFiles\ 和 Certificates\。 当项类型 OutputDir 用作 OutputDirList 属性的值时,MSBuild 会将项类型转换或“平展”到用分号分隔的字符串“KeyFiles\;Certificates\”。
任务中的属性和项
对于 MSBuild 任务,属性和项用作输入和输出。 有关详细信息,请参阅任务。
属性将作为特性传递给任务。 在该任务中,MSBuild 属性由其值可转换为字符串和可由字符串转化得到的属性类型表示。 受支持的属性类型包括 bool
、char
、DateTime
、Decimal
、Double
、int
、string
,以及任何 ChangeType 可以处理的类型。
项作为 ITaskItem 对象传递给任务。 在该任务中,ItemSpec 表示项的值,GetMetadata 检索其元数据。
项类型的项列表可以作为 ITaskItem
对象的数组传递。 从 .NET Framework 3.5 开始,可以使用 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>
执行 Message 任务将显示此消息:
KeyFileVersion: 1.0.0.3
这是因为 KeyFileVersion
的值实际是字符串“@(KeyFile->'%(Version)')”。 第一次定义属性时,项和项转换不会展开,因此 KeyFileVersion
属性分配到未扩展字符串的值。
在生成的执行阶段期间,处理 Message 任务时,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>
Message 任务将显示此消息:
KeyFileVersion:
这是因为在生成的执行阶段期间,将同时自上而下地评估目标内定义的属性和项组。 当 KeyFileVersion
得以定义时,KeyFile
是未知的。 因此,项转换将展开为空字符串。
在这种情况下,反转属性和项组的顺序将还原原始消息:
<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->'%(Version)')”。 Message 任务将显示此消息:
KeyFileVersion: 1.0.0.3