实现“拼好库”,让你的 NuGet 包同时支持库调用和源生成器解析
实现“拼好库”,让你的 NuGet 包同时支持库调用和源生成器解析
CommunityToolkit.Mvvm 同时能够被调用和使用源生成器,但是它是如何实现的呢?本文将提供一个简单的教程。
一、省流
其实像 CommunityToolkit.Mvvm 这样的包看似是一个单独的包,其实是由两部分组成的。
一个项目是无法实现同时支持 API 调用和源生成器的,而实现它们的秘诀就是在 csproj 将两个项目“黏”起来。
二、准备“拼好库”
我们需要准备以下三个项目:
- 纯源生成器项目
- 纯类库项目
- 测试用的控制台(用来看我们的源生成器和类库载入是否生效了)
三、纯源生成器项目的大致代码
这个源生成器的功能是解析项目中名为
hello.txt的附加文件(一定是附加文件!附加文件的概念在我们后面第五节有详细的说明),它会解析其中的文本,并生成一个对应文本作为类名的空的类,第五节我们可以看到相关的效果图。
我们把项目叫做:HelloGen。

csproj的部分是这样的。
可能需要配置
<IncludeBuiltOutputGroup>false</IncludeBuiltOutputGroup>
<Target Name="EnsureHelloGenBuilt" BeforeTargets="Pack">
<Error Condition="!Exists('..\HelloGen\HelloGen\bin\$(Configuration)\netstandard2.0\HelloGen.dll')"
Text="HelloGen.dll not found. Please build HelloGen project first." />
</Target>
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsRoslynComponent>true</IsRoslynComponent>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.11.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.14.0" PrivateAssets="all" />
</ItemGroup>
</Project>
这个项目有且仅有一个源生成器:
using Microsoft.CodeAnalysis;
using System.IO;
using System.Linq;
namespace HelloGen
{
[Generator]
public class OHHHGen : IIncrementalGenerator
{
public void Initialize(IncrementalGeneratorInitializationContext context)
{
var hello = context.AdditionalTextsProvider.Where(i => Path.GetFileName(i.Path) == "hello.txt").Collect();
context.RegisterSourceOutput(hello, (ctx, arr) =>
{
foreach (var item in arr)
{
var text = item.GetText();
ctx.AddSource($"{text}.g.cs", $"public class {text}{{public int OHHHHH = 0;}}");
}
});
}
}
}
四、纯类库的内容
我们的类库名为 HelloLib,它的结构也及其的简单。

其中,HelloClass 的内容实在是太简单了,就不另外贴代码了,各位看截图吧。

重点在于 csproj 的部分喵。
重点如下!
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<None Include="..\HelloGen\bin\$(Configuration)\netstandard2.0\HelloGen.dll"
PackagePath="analyzers\dotnet\roslyn4.0\cs"
Pack="true"
Visible="false" />
</ItemGroup>
</Project>
特别是其中的 <None Include="xxxx.dll" PackagePath="analyzers\dotnet\roslyn4.0\cs" Pack="true" Visible="false" /> 这一段。
这一段我是抄自 CommunityToolkit.Mvvm 的,实际上似乎 PackagePath="analyzers/dotnet/cs" 这样的写法也没有问题,可以参考 https://roslyn-analyzers.readthedocs.io/en/latest/create-nuget-package.html 上面的说法喵。
通过这个配置,你就可以将两者拼起来了。
当你完成了这两个之后你就可以导出成 NuGet 包了。
就像这样,至于是 Debug 还是 Release 无所谓的。

五、测试用的控制台
总之你要一个控制台,然后安装上我们的 NuGet 包,在我们项目就是 HelloLib。
这是我们的结构。

1. 创建一个 hello.txt 的附加文件
总之你需要先放一个 hello.txt 在这里,然后,csproj 的地方你可以参考这个:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net9.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="hello.txt" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="HelloLib" Version="1.0.0" />
</ItemGroup>
</Project>
你有注意到 AdditionalFiles 那一块了吗?
文本的内容非常的简单:

简单的一句 HelloWorld。
2. 观察源生成器的反应
我们打开分析器,你可以发现这里有一个 HelloWorld.g.cs。

生成的代码如下:
public class HelloWorld{public int OHHHHH = 0;}
3. 直接在代码中使用吧

你可以发现在智能提示中有自动生成的 HelloWorld 类,当然也包括来自纯类库的 HelloLib。
可喜可贺啊。我们也把代码也会贴到了 gitee 中哦。
六、总结
为了实现这样的“拼好库”,<None Include="xxxx.dll" PackagePath="analyzers\dotnet\roslyn4.0\cs" Pack="true" Visible="false" /> 这一段真的非常重要哦喵。

浙公网安备 33010602011771号