在 SDK 格式的项目文件可以通过简单代码引用某个文件夹里面指定后缀的文件作为项目文件

例如我想要引用相对于 csproj 的上一层文件夹里面的 doubi 文件夹里面的所有 cs 文件,作为链接引用的方法,可以使用下面代码

  <ItemGroup>
    <Compile Include="..\doubi\*.cs" Link="lindexi.blog.csdn.net\%(FileName)%(Extension)" />
  </ItemGroup>

上面代码用到了 FileName 可以替换每一项的文件名,加上 Extension 就能表示路径

按照上面的代码,其实后缀名也是根据引用的文件自动添加,那么如何添加某个文件夹里面的所有 png 文件

差不多的逻辑也就能写出添加某个项目里面的所有 png 文件

  <ItemGroup>
    <Content Include="..\Tool.UWP\Assets\*.png" Link="Assets\%(FileName)%(Extension)" />
  </ItemGroup>

如果想要引用文件夹里面的所有内容,即使这些内容在文件夹的文件夹里面,也就是不在顶层文件夹,可以这样写

  <ItemGroup>
    <Content Include="..\Tool.UWP\Assets\**\*.png" Link="Assets\%(FileName)%(Extension)" />
  </ItemGroup>

可以看到上面代码添加了 **\*.png 其中的 ** 表示任意一层文件夹

而有时候包含了 obj 等不期望加入的文件,可以通过 Exclude 输入,请看以下示例

Exclude="..\Tool.UWP\obj\**\*;"

大概的代码如下

  <ItemGroup>
    <Content Include="..\Tool.UWP\Assets\**\*.png" Exclude="..\Tool.UWP\obj\**\*;..\Tool.UWP\Foo\**\*" />
  </ItemGroup>

如果只是期望引用某个文件夹里面的某几个文件,可以参阅 如何在 MSBuild 中正确使用 % 来引用每一个项(Item)中的元数据 - walterlv 的写法。先写一个自定义项用来接收需要特殊指定的文件,接着再写一个中转使用的自定义项来拼接,最后加入到引用即可。如以下代码

  <ItemGroup>
    <FooAddFileName Include="1.png"/>
    <FooAddFileName Include="2.png"/>
    <FooAddFileName Include="3.png"/>

    <__FooAddFileName Include="@(FooAddFileName)">
      <ReferenceFilePath>..\Tool.UWP\Assets\%(Identity)</ReferenceFilePath>
    </__FooAddFileName>

    <Content Include="@(__FooAddFileName->'%(ReferenceFilePath)')" Link="Assets\%(FileName)%(Extension)" />
  </ItemGroup>

以上代码自定义了为 FooAddFileName 的自定义项用来接收需要特殊指定的文件。如以上代码指定了 1.png 等特殊几个文件。接着定义了名为 __FooAddFileName 的自定义项用来中转,凭借上文件夹路径。最后再加入引用

如果不想让加入的项目在 VisualStudio 里可见,可加上 Visible="False" 属性,如以下代码

  <ItemGroup>
    <None Include="..\MauiApp\bin\Debug\net6.0\**\*" CopyToOutputDirectory="PreserveNewest" Visible="False"></None>
  </ItemGroup>

如需保持相对路径关系,可采用 %(RecursiveDir) 属性,如以下例子,更多请看 项目文件中的已知属性(知道了这些,就不会随便在 csproj 中写死常量啦) - walterlv

<Compile Include="..\..\DocumentFormat.OpenXml.Flatten\src\DocumentFormat.OpenXml.Flatten\**\*.cs" Exclude="**\bin\**\*.cs;**\obj\**\*.cs" Link="DocumentFormat.OpenXml.Flatten\%(RecursiveDir)\%(FileName)%(Extension)" />

如果只是为了放入到某个文件夹里面,可以使用 LinkBase 特性辅助,例如全部放入到 Shared 文件夹,例子如下

<ItemGroup>
  <Content Include="..\Extras\**\*.cs" LinkBase="Shared"/>
</ItemGroup>

使用之后的例子效果如下

通过这个方法可以将原本一个大的项目,才分为多个小的项目,每个小项目独立,但是最终打包的项目将通过此 方式引用所有的小项目。同时打包的时候不仅主项目会打包,每个小的项目都可以独立打包,这样做的优势是可以提升每个小项目的内聚和降低项目之间的耦合

我现在将很多基础的工具库都用此方式管理,我将一个工具库拆分为很多个小的工具库,每个工具库只包含很小的功能,但是同时我也创建一个主项目,这个主项目里面只放很少的代码,主要代码都是通过本文的方式引用所有小工具库的代码,这样打包出来的主项目是包含全部功能的。但是这个主项目的大小居然有3M这么大了,里面全部都是代码,很多项目里面根本不需要用到这么大的工具项目,于是这些项目就可以使用小的工具库

如果此时我需要生成不同的平台的库呢?例如某个工具库我只是支持 .NET Framework 的,某个工具库我只支持 WPF 的

此时通过宏定义的方式就可以让合并到一起的主项目按照输出的不同的 NuGet 库添加不同的代码

另外我推荐使用 SourceYard 的方式制作源代码包,这样每个小的工具库被引用的时候是通过源代码被引用,这样就不会添加额外的引用文件

添加额外的引用文件将会降低软件的启动性能,详细测试请看 C# 程序集数量对软件启动性能的影响

如何使用 SourceYard 做源代码包请看 SoureYard 官方开源项目

更多编译相关请看手把手教你写 Roslyn 修改编译


本文会经常更新,请阅读原文: https://blog.lindexi.com/post/Roslyn-%E5%BC%95%E7%94%A8%E6%9F%90%E4%B8%AA%E6%96%87%E4%BB%B6%E5%A4%B9%E6%89%80%E6%9C%89%E6%96%87%E4%BB%B6%E4%BD%9C%E4%B8%BA%E9%93%BE%E6%8E%A5.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

如果你想持续阅读我的最新博客,请点击 RSS 订阅,推荐使用RSS Stalker订阅博客,或者收藏我的博客导航

知识共享许可协议 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名林德熙(包含链接: https://blog.lindexi.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系

微软最具价值专家


无盈利,不卖课,做纯粹的技术博客

以下是广告时间

推荐关注 Edi.Wang 的公众号

欢迎进入 Eleven 老师组建的 .NET 社区

以上广告全是友情推广,无盈利