【问题标题】:How do I replace a string variable with a var in Roslyn?如何在 Roslyn 中用 var 替换字符串变量?
【发布时间】:2014-04-22 18:40:07
【问题描述】:

对于像这样的本地声明: 字符串 a = string.Empty;

如何编写诊断以将其更改为: var a = string.Empty;

【问题讨论】:

  • 你的意思是没有赋值只是声明像string a;?
  • 究竟是什么让你绊倒了?这对于 StackOverflow 问题来说太宽泛了。获取 SDK 并查看示例。
  • 幸运的是,我碰巧基于this project 构建了自己的诊断工具,它已经实现了您感兴趣的功能。

标签: c# roslyn diagnostics


【解决方案1】:

你不能。 var 关键字告诉编译器执行类型推断,仅使用 var a; 编译器没有足够的信息来推断类型。

您可以执行以下任何操作

var a = new String();
var b = String.Empty;
var c = "";

但这似乎比付出更多的努力。

编辑更新请求:为什么要修改所有要使用 var 声明的代码?无论如何,它都会编译为相同的 IL(非常简单的示例):

// var a = String.Empty;
IL_0000:  ldsfld     string [mscorlib]System.String::Empty
IL_0005:  pop
// string b = String.Empty;
IL_0006:  ldsfld     string [mscorlib]System.String::Empty
IL_000b:  pop

【讨论】:

  • var 仍然是强类型的,编译器需要在编译时弄清楚它是什么类型。
  • 我后来意识到它只添加了我的评论,然后删除了。
  • 你完全误解了这个问题;他在询问 Roslyn 语法 API。
  • 原发帖人改了问题,所以内森的回答没有意义了。 (这本来是完全合理的。)
【解决方案2】:

我已经整理了一个带有诊断功能的代码修复程序。 以下是有趣的部分:

ISyntaxNodeAnalyzer中AnalyzeNode的实现

public void AnalyzeNode(SyntaxNode node, SemanticModel semanticModel, Action<Diagnostic> public void AnalyzeNode(SyntaxNode node, SemanticModel semanticModel, Action<Diagnostic> addDiagnostic, CancellationToken cancellationToken)
    {
        var localDeclaration = (LocalDeclarationStatementSyntax)node;
        if (localDeclaration.Declaration.Type.IsVar) return;
        var variable = localDeclaration.Declaration.Variables.First();
        var initialiser = variable.Initializer;
        if (initialiser == null) return;
        var variableTypeName = localDeclaration.Declaration.Type;
        var variableType = semanticModel.GetTypeInfo(variableTypeName).ConvertedType;
        var initialiserInfo = semanticModel.GetTypeInfo(variable.Initializer.Value);
        var typeOfRightHandSideOfDeclaration = initialiserInfo.Type;
        if (Equals(variableType, typeOfRightHandSideOfDeclaration))
        {
            addDiagnostic(Diagnostic.Create(Rule, node.GetLocation(), localDeclaration.Declaration.Variables.First().Identifier.Value));
        }
    }

这实质上是查看声明两边的类型,如果它们相同(并且 RHS 还不是 var),则添加诊断。

这是代码修复的代码:

public async Task<IEnumerable<CodeAction>> GetFixesAsync(Document document, TextSpan span, IEnumerable<Diagnostic> diagnostics, CancellationToken cancellationToken)
    {
        var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
        var diagnosticSpan = diagnostics.First().Location.SourceSpan;
        var declaration = root.FindToken(diagnosticSpan.Start).Parent.AncestorsAndSelf().OfType<LocalDeclarationStatementSyntax>().First();
        return new[] { CodeAction.Create("Use var", c => ChangeDeclarationToVar(document, declaration, c)) };
    }

private async Task<Document> ChangeDeclarationToVar(Document document, LocalDeclarationStatementSyntax localDeclaration, CancellationToken cancellationToken)
    {
        var root = await document.GetSyntaxRootAsync(cancellationToken);
        var variableTypeName = localDeclaration.Declaration.Type;
        var varTypeName = SyntaxFactory.IdentifierName("var").WithAdditionalAnnotations(Formatter.Annotation);
        var newDeclaration = localDeclaration.ReplaceNode(variableTypeName, varTypeName);            
        var newRoot = root.ReplaceNode(localDeclaration, newDeclaration);
        return document.WithSyntaxRoot(newRoot);
    }

这一点很好很简单,只需从语法工厂中获取 var 并将其切换出来。 请注意, var 在 SyntaxFactory 中没有自己的静态方法,因此是按名称引用。

【讨论】:

  • 正是我要找的……我唯一缺少的是: var root = await document.GetSyntaxRootAsync(cancellationToken);这是干什么用的?
  • 这会从文档中获取主要的语法块并包含所有节点。然后它被向下使用以用新节点替换目标节点,这将返回具有新节点的整个根。这就像返回一个节点列表,因此您可以访问要替换的节点。
【解决方案3】:

编译器无法从中推断出类型。

你需要使用:

var a = ""; // compiler can see that is type `string`:

或者你可以这样做:

string a;

【讨论】:

  • 原始发帖人询问如何使用 Roslyn API 进行这种转换。
猜你喜欢
  • 1970-01-01
  • 2014-12-22
  • 1970-01-01
  • 1970-01-01
  • 2011-10-23
  • 2020-07-09
  • 2021-08-03
  • 2022-08-09
  • 1970-01-01
相关资源
最近更新 更多