【问题标题】:Getting Class FullName (including namespace) from Roslyn ClassDeclarationSyntax从 Roslyn ClassDeclarationSyntax 获取类全名(包括命名空间)
【发布时间】:2021-09-26 03:25:33
【问题描述】:

我有一个来自 roslyn 语法树的 ClassDeclarationSyntax。 我是这样读的:

var tree = SyntaxTree.ParseText(sourceCode);
var root = (CompilationUnitSyntax)tree.GetRoot();

var classes = root.DescendantNodes().OfType<ClassDeclarationSyntax>();

标识符仅包含类的名称,但没有有关命名空间的信息,因此缺少 fullType Name。像“MyClass”,但不是“Namespace1.MyClass”

获取语法的命名空间/FulltypeName 的推荐方法是什么?

【问题讨论】:

  • AFAIK,该信息来自语义树。
  • 我担心你对我找到的信息的权利:social.msdn.microsoft.com/Forums/vstudio/en-US/… 但这对我来说似乎很难看。在我的情况下,如果壮举会花费很多性能:-(
  • 正如那里提到的,您可以通过递归查找命名空间声明来自己构建它。当心嵌套的泛型类。
  • 我通过递归查找解决方案来重读该解决方案。在我的场景中,我可以排除嵌套的泛型类。非常感谢。
  • ...使用语义,这确实是正确的方法。至少在 VB.NET 代码中,任何从语法上确定它的尝试在常见情况下都会被破坏。

标签: c# roslyn


【解决方案1】:

你可以使用我写的帮助类来做到这一点:

NamespaceDeclarationSyntax namespaceDeclarationSyntax = null;
if (!SyntaxNodeHelper.TryGetParentSyntax(classDeclarationSyntax, out namespaceDeclarationSyntax))
{
    return; // or whatever you want to do in this scenario
}

var namespaceName = namespaceDeclarationSyntax.Name.ToString();
var fullClassName = namespaceName + "." + classDeclarationSyntax.Identifier.ToString();

和助手:

static class SyntaxNodeHelper
{
    public static bool TryGetParentSyntax<T>(SyntaxNode syntaxNode, out T result) 
        where T : SyntaxNode
    {
        // set defaults
        result = null;

        if (syntaxNode == null)
        {
            return false;
        }

        try
        {
            syntaxNode = syntaxNode.Parent;

            if (syntaxNode == null)
            {
                return false;
            }

            if (syntaxNode.GetType() == typeof (T))
            {
                result = syntaxNode as T;
                return true;
            }

            return TryGetParentSyntax<T>(syntaxNode, out result);
        }
        catch
        {
            return false;
        }
    }
}

这里没有什么太复杂的事情......命名空间将在语法树“向上”是有道理的(因为该类包含在命名空间中)所以您只需要在语法树“向上”移动直到找到命名空间并将其附加到 ClassDeclarationSyntax 的标识符。

【讨论】:

  • 你为什么不只是 var namespace = root.DescendantNodes().OfType();
  • 不确定现在是否可行,但我认为当时不支持 - 我已经有一段时间没有与 Roslyn 合作了
  • 当我尝试在嵌套类的场景中使用它时,我意识到所有嵌套类都使用这种方法。 namespace MyNsmespace { class MyOuterClass { public class MyInnerClass { // ... } } } 应该解析为 MyNamespace.MyOuterClass+MyInnerClass
  • @johnny5 我认为DescendantNodes 是语法树中的错误方向,因为它向下而不是向上。我通过使用syntaxNode.Ancestors().OfType&lt;NamespaceDeclarationSyntax&gt;(); 解决了它
  • 嗯,op 正在寻找后代,但是您可以选择任何一种方式来寻找解决方案
【解决方案2】:

我知道我在游戏中比较晚,但我偶然发现了one of given the answers,这在我必须处理嵌套类的情况下不起作用。因此,这里有一个也可以处理嵌套类的方法:

public static class ClassDeclarationSyntaxExtensions
{
    public const string NESTED_CLASS_DELIMITER = "+";
    public const string NAMESPACE_CLASS_DELIMITER = ".";

    public static string GetFullName(this ClassDeclarationSyntax source)
    {
        Contract.Requires(null != source);

        var items = new List<string>();
        var parent = source.Parent;
        while (parent.IsKind(SyntaxKind.ClassDeclaration))
        {
            var parentClass = parent as ClassDeclarationSyntax;
            Contract.Assert(null != parentClass);
            items.Add(parentClass.Identifier.Text);

            parent = parent.Parent;
        }

        var nameSpace = parent as NamespaceDeclarationSyntax;
        Contract.Assert(null != nameSpace);
        var sb = new StringBuilder().Append(nameSpace.Name).Append(NAMESPACE_CLASS_DELIMITER);
        items.Reverse();
        items.ForEach(i => { sb.Append(i).Append(NESTED_CLASS_DELIMITER); });
        sb.Append(source.Identifier.Text);

        var result = sb.ToString();
        return result;
    }
}

【讨论】:

    【解决方案3】:

    基于the answer by Ronald,我添加了对嵌套命名空间、结构体和泛型的支持。

    它使用generics CLR naming convention 允许将输出用作Compilation.GetTypeByMetadataName() 的输入

    public static class TypeDeclarationSyntaxExtensions
    {
        const char NESTED_CLASS_DELIMITER = '+';
        const char NAMESPACE_CLASS_DELIMITER = '.';
        const char TYPEPARAMETER_CLASS_DELIMITER = '`';
    
        public static string GetFullName(this TypeDeclarationSyntax source)
        {
            if (source is null)
                throw new ArgumentNullException(nameof(source));
    
            var namespaces = new LinkedList<NamespaceDeclarationSyntax>();
            var types = new LinkedList<TypeDeclarationSyntax>();
            for (var parent = source.Parent; parent is object; parent = parent.Parent)
            {
                if (parent is NamespaceDeclarationSyntax @namespace)
                {
                    namespaces.AddFirst(@namespace);
                }
                else if (parent is TypeDeclarationSyntax type)
                {
                    types.AddFirst(type);
                }
            }
    
            var result = new StringBuilder();
            for (var item = namespaces.First; item is object; item = item.Next)
            {
                result.Append(item.Value.Name).Append(NAMESPACE_CLASS_DELIMITER);
            }
            for (var item = types.First; item is object; item = item.Next)
            {
                var type = item.Value;
                AppendName(result, type);
                result.Append(NESTED_CLASS_DELIMITER);
            }
            AppendName(result, source);
    
            return result.ToString();
        }
    
        static void AppendName(StringBuilder builder, TypeDeclarationSyntax type)
        {
            builder.Append(type.Identifier.Text);
            var typeArguments = type.TypeParameterList?.ChildNodes()
                .Count(node => node is TypeParameterSyntax) ?? 0;
            if (typeArguments != 0)
                builder.Append(TYPEPARAMETER_CLASS_DELIMITER).Append(typeArguments);
        }
    }
    

    【讨论】:

      【解决方案4】:

      试试这个代码

      public static string GetFullName(NamespaceDeclarationSyntax node)
      {
          if (node.Parent is NamespaceDeclarationSyntax)
              return String.Format("{0}.{1}",
                  GetFullName((NamespaceDeclarationSyntax)node.Parent),
                  ((IdentifierNameSyntax)node.Name).Identifier.ToString());
          else
              return ((IdentifierNameSyntax)node.Name).Identifier.ToString();
      }
      

      【讨论】:

      • 你为什么要使用 NamespaceDeclarationSyntax 而不仅仅是一个语法节点
      • 它是 CSharpSyntaxWalker 后代的一部分,带有 owerriden VisitNamespaceDeclaration(NamespaceDeclarationSyntax node)。
      【解决方案5】:

      使用模式匹配+递归还有一种优雅的方式:

      // method with pattern matching
      public static string GetNamespaceFrom(SyntaxNode s) =>
          s.Parent switch
          {
              NamespaceDeclarationSyntax namespaceDeclarationSyntax => namespaceDeclarationSyntax.Name.ToString(),
              null => string.Empty, // or whatever you want to do
              _ => GetNamespaceFrom(s.Parent)
          };
      
      // somewhere call it passing the class declaration syntax:
      string ns = GetNamespaceFrom(classDeclarationSyntax);
      

      【讨论】:

      • 看起来这不支持没有大括号 ala .net 6 的命名空间
      【解决方案6】:

      这是我获取命名空间的方法。您必须针对您的情况稍微修改我的代码:

              public static async Task<NamespaceDeclarationSyntax> GetNamespaceAsync(this Document document, CancellationToken cancellationToken = default(CancellationToken))
              {
                  SyntaxNode documentRoot = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
                  var rootCompUnit = (CompilationUnitSyntax)documentRoot;
                  return (NamespaceDeclarationSyntax)rootCompUnit.Members.Where(m => m.IsKind(SyntaxKind.NamespaceDeclaration)).Single();
              }
      

      【讨论】:

      • 我认为这只是故事的一半。如果命名空间是嵌套的怎么办?我建议您一直往上走,直到您到达起点。
      【解决方案7】:

      另一个答案.. 这个实用程序类也支持嵌套命名空间和嵌套类。

      public static class SyntaxNodeHelper
      {
          public static string GetPrefix(SyntaxNode member)
          {
              if (member == null) {
                  return "";
              }
      
              StringBuilder sb = new StringBuilder();
              SyntaxNode node = member;
      
              while(node.Parent != null) {
                  node = node.Parent;
      
                  if (node is NamespaceDeclarationSyntax) {
                      var namespaceDeclaration = (NamespaceDeclarationSyntax) node;
      
                      sb.Insert(0, ".");
                      sb.Insert(0, namespaceDeclaration.Name.ToString());
                  } else if (node is ClassDeclarationSyntax) {
                      var classDeclaration = (ClassDeclarationSyntax) node;
      
                      sb.Insert(0, ".");
                      sb.Insert(0, classDeclaration.Identifier.ToString());
                  }
              }
      
              return sb.ToString();
          }
      }
      

      【讨论】:

        【解决方案8】:

        这段代码可以解决你的问题。

        public static string ClassFullName(this ClassDeclarationSyntax varClassDec)
        {
            SyntaxNode tempCurCls = varClassDec;
            var tempFullName = new Stack<string>();
            
            do
            {
                if (tempCurCls.Kind() == SyntaxKind.ClassDeclaration)
                {
                    tempFullName.Push(((ClassDeclarationSyntax)tempCurCls).Identifier.ToString());
                }
                else if (tempCurCls.Kind() == SyntaxKind.NamespaceDeclaration)
                {
                    tempFullName.Push(((NamespaceDeclarationSyntax)tempCurCls).Name.ToString());
                }
                
                tempCurCls = tempCurCls.Parent;
            } while (tempCurCls != null);
            
            return string.Join(".", tempFullName);
        }
        

        【讨论】:

        • 您的答案可以通过额外的支持信息得到改进。请edit 添加更多详细信息,例如引用或文档,以便其他人可以确认您的答案是正确的。你可以找到更多关于如何写好答案的信息in the help center
        猜你喜欢
        • 2015-06-11
        • 1970-01-01
        • 2011-07-13
        • 2015-06-02
        • 2017-03-04
        • 2019-03-12
        • 2019-06-26
        • 2010-12-08
        • 1970-01-01
        相关资源
        最近更新 更多