You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Roslyn代码修复中条件添加using语句及程序集引用检测问题

检测程序集引用并调整Roslyn代码修复逻辑

我刚好做过类似的Roslyn分析器场景,这个检测程序集引用的问题其实可以通过利用Compilation对象检查目标类型是否存在来解决,比直接遍历程序集引用更可靠,因为它能直接反映类型是否可被当前项目访问。

核心思路

Roslyn的Compilation对象包含了当前项目所有可访问的类型和程序集信息。我们可以通过查找Trilogy.Miscellaneous.StringExtensions这个类型是否存在,来间接判断项目是否引用了Trilogy.Common程序集:

  • 如果类型存在:说明项目已引用,继续使用StringExtensions.SafeJoin的修复逻辑,同时自动添加using。
  • 如果类型不存在:说明未引用,要么不提供该修复,要么切换到string.Join的备选方案。

具体实现步骤

在你的代码修复提供者(CodeFixProvider)的RegisterCodeFixesAsync方法中,加入以下逻辑:

  1. 获取当前文档的SemanticModel和Compilation对象:
var semanticModel = await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false);
var compilation = semanticModel.Compilation;
  1. 检查目标类型是否存在:
// 注意这里要用完全限定的元数据名称(命名空间+类型名)
var stringExtensionsType = compilation.GetTypeByMetadataName("Trilogy.Miscellaneous.StringExtensions");
var hasTrilogyReference = stringExtensionsType != null;
  1. 根据检测结果分支处理修复逻辑:
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var diagnostic = context.Diagnostics.First();
var diagnosticSpan = diagnostic.Location.SourceSpan;
var formatInvocation = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<InvocationExpressionSyntax>().First();

if (hasTrilogyReference)
{
    // 原有修复:替换为SafeJoin并添加using
    context.RegisterCodeFix(
        CodeAction.Create(
            title: "Replace string.Format with StringExtensions.SafeJoin",
            createChangedDocument: c => ReplaceWithSafeJoinAsync(context.Document, formatInvocation, c),
            equivalenceKey: "ReplaceWithSafeJoin"),
        diagnostic);
}
else
{
    // 备选方案1:不提供修复(直接跳过,不注册CodeAction)
    // return;

    // 备选方案2:替换为string.Join
    context.RegisterCodeFix(
        CodeAction.Create(
            title: "Replace string.Format with string.Join",
            createChangedDocument: c => ReplaceWithStringJoinAsync(context.Document, formatInvocation, c),
            equivalenceKey: "ReplaceWithStringJoin"),
        diagnostic);
}

关键细节说明

  • 为什么用GetTypeByMetadataName而不是检查程序集引用?
    直接检查程序集引用可能会有误差(比如程序集被引用但类型被移除、或者有别名导致无法访问),而检查类型是否能被编译系统找到,是最直接判断该类型是否可用的方式。
  • 确保元数据名称正确:要使用类型的完全限定名,包括命名空间,比如Trilogy.Miscellaneous.StringExtensions,不能写错。
  • using指令的添加逻辑:只有在hasTrilogyReference为true时,才执行添加using Trilogy.Miscellaneous的代码,避免无效的using导入。

补充:替换为string.Join的代码生成示例

如果选择备选方案2,生成string.Join的代码逻辑可以参考这样:

private async Task<Document> ReplaceWithStringJoinAsync(Document document, InvocationExpressionSyntax formatInvocation, CancellationToken cancellationToken)
{
    var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
    
    // 解析原string.Format的参数:格式字符串、A、B
    var formatArgs = formatInvocation.ArgumentList.Arguments;
    var separatorLiteral = SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(" "));
    var joinArgs = SyntaxFactory.ArgumentList(
        SyntaxFactory.SeparatedList(new[]
        {
            SyntaxFactory.Argument(separatorLiteral),
            formatArgs[1],
            formatArgs[2]
        }));
    
    // 构建string.Join的调用
    var joinInvocation = SyntaxFactory.InvocationExpression(
        SyntaxFactory.MemberAccessExpression(
            SyntaxKind.SimpleMemberAccessExpression,
            SyntaxFactory.IdentifierName("string"),
            SyntaxFactory.IdentifierName("Join")),
        joinArgs);
    
    // 替换原节点
    var newRoot = root.ReplaceNode(formatInvocation, joinInvocation);
    return document.WithSyntaxRoot(newRoot);
}

这样就能根据项目是否引用Trilogy.Common,动态提供合适的代码修复选项了。

内容的提问来源于stack exchange,提问作者Cameron Peters

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.29 07:10:55