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

如何使用Microsoft.CodeAnalysis替换不同类型的NameSyntax节点?

解决Roslyn替换不同类型语法节点的异常问题

问题场景

环境为.NET Framework 4.6.1(不可更改版本),在代码版本转换时,需将属性名LastBarIndexPainted替换为ChartBars.ToIndex,但调用CodeAnalysis.ReplaceNodes()时抛出异常:

Unable to cast object of type 'Microsoft.CodeAnalysis.CSharp.Syntax.QualifiedNameSyntax' to type 'Microsoft.CodeAnalysis.CSharp.Syntax.SimpleNameSyntax'

原因是ReplaceNodes()要求替换前后的语法节点类型必须一致:LastBarIndexPainted属于SimpleNameSyntax类型,而ChartBars.ToIndex是QualifiedNameSyntax类型,直接替换因类型不匹配触发报错。

当前替换字典

private Dictionary<string, string> _renamedProperties = new Dictionary<string, string>
{
    { "CalculateOnBarClose", "Calculate" },
    { "LastBarIndexPainted", "ChartBars.ToIndex" },
};

原转换函数

private SyntaxNode ReplacePropertyNames(SyntaxNode node, Dictionary<string, string> propertyNameLookup)
{
    IEnumerable<NameSyntax> propertyIdentifiers = node
        .DescendantNodes()
        .OfType<NameSyntax>()
        .Where(n => propertyNameLookup.ContainsKey(n.ToString()));

    var replacements = new Dictionary<SyntaxNode, SyntaxNode>();
    SyntaxNode newIdentifierName2 = null;
    foreach (NameSyntax identifierName in propertyIdentifiers)
    {
        string newName = propertyNameLookup[identifierName.ToString()];
        SyntaxNode newIdentifierName = SyntaxFactory.ParseName(newName).WithTriviaFrom(identifierName);
        replacements.Add(identifierName, newIdentifierName); newIdentifierName2 = newIdentifierName;
    }

    return node.ReplaceNodes(replacements.Keys, (n, _) => replacements[n]);  // 抛出异常处
}

解决方案

核心思路是不直接替换NameSyntax节点,而是定位属性名所在的父节点上下文,根据场景替换对应合法的语法节点,修改后的转换函数如下:

private SyntaxNode ReplacePropertyNames(SyntaxNode node, Dictionary<string, string> propertyNameLookup)
{
    // 筛选所有需要替换的SimpleNameSyntax节点
    var targetNodes = node
        .DescendantNodes()
        .OfType<SimpleNameSyntax>()
        .Where(s => propertyNameLookup.ContainsKey(s.Identifier.ValueText));

    var replacements = new Dictionary<SyntaxNode, SyntaxNode>();

    foreach (var simpleName in targetNodes)
    {
        string newName = propertyNameLookup[simpleName.Identifier.ValueText];
        var newNameSyntax = SyntaxFactory.ParseName(newName).WithTriviaFrom(simpleName);

        // 根据父节点类型分场景处理
        if (simpleName.Parent is MemberAccessExpressionSyntax memberAccess)
        {
            // 若属性是成员访问的一部分(如obj.LastBarIndexPainted),替换整个成员访问表达式
            var newMemberAccess = memberAccess.WithName(newNameSyntax);
            replacements.Add(memberAccess, newMemberAccess);
        }
        else
        {
            // 若属性是单独标识符(如直接使用LastBarIndexPainted),直接替换节点
            replacements.Add(simpleName, newNameSyntax);
        }
    }

    return node.ReplaceNodes(replacements.Keys, (original, _) => replacements[original]);
}

修改说明

  1. 缩小目标范围:仅针对SimpleNameSyntax节点筛选,避免匹配其他类型的NameSyntax。
  2. 父节点上下文适配:
    • 当属性是成员访问表达式的一部分时,替换整个MemberAccessExpressionSyntax节点,确保语法结构合法。
    • 当属性是单独使用的标识符时,直接替换为新的限定名节点。
  3. 保留格式细节:通过WithTriviaFrom保留原节点的注释、空格等格式信息。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 11:00:07