如何通过Roslyn获取一个类的基类名称?

4

我正在使用Roslyn,并且我有以下类:

var source = @"
    using System;
    class MyClass : MyBaseClass {
      static void Main(string[] args) {
        Console.WriteLine(""Hello, World!"");
      }
    }";

// Parsing
SyntaxTree tree = CSharpSyntaxTree.ParseText(source);

// This uses an internal function (working)
// That gets the first node of type `SimpleBaseTypeSyntax`
SimpleBaseTypeSyntax simpleBaseType = GetNBaseClassNode(tree);

获取基类名称

我已经成功访问了包含我需要的内容的节点SimpleBaseTypeSyntax。实际上,如果我使用语法资源管理器,我会得到以下结果:

enter image description here

节点IdentifierTokenTextValueValueText属性均为"MyBaseClass",这正是我所需的!

然而,虽然在语法资源管理器中我可以看到所有这些值,但我无法通过编程方式访问它们。

因此,我尝试以编程方式检索节点:

IdentifierNameSyntax identifierNode =
  simpleBaseType.ChildNodes().OfType<IdentifierNameSyntax>().First();
SyntaxToken identifier = simpleBaseType.Identifier;
string name = identifier.Text;

但是name是空字符串。与identifier.Valueidentifier.ValueText相同。

我做错了什么?也许我做错了,那么你将如何检索基类名称?


另一种尝试:使用语义模型

我开始思考我需要这种类型信息的语义模型:

IdentifierNameSyntax identifierNode =
  simpleBaseType .ChildNodes().OfType<IdentifierNameSyntax>().First();

SemanticModel semanticModel =
  CSharpCompilation.Create("Class")
   .AddReferences(MetadataReference.CreateFromFile(
     typeof(object).Assembly.Location))
       .AddSyntaxTrees(tree).GetSemanticModel(tree);

SymbolInfo symbolInfo = this.semanticModel.GetSymbolInfo(identifierNode);
string name = symbolInfo.Symbol.Name;

symbolInfo.Symbol 为空时,会抛出异常。


首先,您可能需要语义模型。 - SLaks
如果您在创建的编译对象上调用GetDiagnostics(),是否存在任何错误?如果设置有误,则可能无法获取任何绑定信息。 - Jason Malinowski
4个回答

13

其实我不知道为什么你不能将BaseTypeSyntax通过GetSymbolInfo()传递给语义模型,但在我的情况下它返回的也是null而没有错误。

无论如何,这里有一个可行的方法:

var tree = CSharpSyntaxTree.ParseText(@"
using System;
class MyBaseClass
{
}
class MyClass : MyBaseClass {
  static void Main(string[] args) {
    Console.WriteLine(""Hello, World!"");
  }
}");

var Mscorlib = PortableExecutableReference.CreateFromAssembly(typeof(object).Assembly);
var compilation = CSharpCompilation.Create("MyCompilation",
    syntaxTrees: new[] { tree }, references: new[] { Mscorlib });
var model = compilation.GetSemanticModel(tree);

var myClass = tree.GetRoot().DescendantNodes().OfType<ClassDeclarationSyntax>().Last();
var myClassSymbol = model.GetDeclaredSymbol(myClass) as ITypeSymbol;
var baseTypeName = myClassSymbol.BaseType.Name;

在这里,您需要使用语义模型,因为在语法层面上,您无法可靠地确定您正在处理接口还是基础类型。


1
现在更改为ISymbol需要一个转换:var myClassSymbol = model.GetDeclaredSymbol(myClass) as ITypeSymbol; - David

5
我看到你正在尝试使用Roslyn API构建分析器。 你知道还有其他测试分析器逻辑的方法吗?可以使用单元测试文件而不是直接将源代码放在分析器中。
采用这种思路,你可以完全使用Visual Studio提供的模板构建分析器,必须从DiagnosticAnalyzer继承并创建分析代码逻辑。
对于你的情况,你应该查看ClassDeclaration并轻松访问节点内的BaseTypes属性。
        public bool SomeTriedDiagnosticMethod(SyntaxNodeAnalysisContext nodeContext)
    {
        var classDeclarationNode = nodeContext.Node as ClassDeclarationSyntax;
        if (classDeclarationNode == null) return false;
        var baseType = classDeclarationNode.BaseList.Types.FirstOrDefault(); //  Better use this in all situations to be sure code won't break
        var nameOfFirstBaseType = baseType.Type.ToString();  
        return nameOfFirstBaseType == "BaseType";

    }

我想在这两个类中获取语法树:JsonPropertyAccess<StylesheetDto>。你知道怎么获取吗? - Bandara

1
    protected static bool IsWebPage(SyntaxNodeAnalysisContext context, ClassDeclarationSyntax classDeclaration)
    {
        INamedTypeSymbol iSymbol = context.SemanticModel.GetDeclaredSymbol(classDeclaration) as INamedTypeSymbol;
        INamedTypeSymbol symbolBaseType = iSymbol?.BaseType;

        while (symbolBaseType != null)
        {
            if (symbolBaseType.ToString() == "System.Web.UI.Page")
                return true;
            symbolBaseType = symbolBaseType.BaseType;
        }

        return false;
    }

1
这是我的助手类,可以找到所有属性、类名、类命名空间和基类。
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace MyProject
{
    public class CsharpClass
    {
        public string Name { get; set; }
        public string Namespace { get; set; }
        public List<CsharpProperty> Properties { get; set; }
        public List<string> BaseClasses { get; set; }

        public class CsharpProperty
        {
            public string Name { get; set; }
            public string Type { get; set; }

            public CsharpProperty(string name, string type)
            {
                Name = name;
                Type = type;
            }
        }

        public CsharpClass()
        {
            Properties = new List<CsharpProperty>();
            BaseClasses = new List<string>();
        }
    }

    public static class CsharpClassParser
    {
        public static CsharpClass Parse(string content)
        {
            var csharpClass = new CsharpClass();
            var tree = CSharpSyntaxTree.ParseText(content);
            var members = tree.GetRoot().DescendantNodes().OfType<MemberDeclarationSyntax>();

            foreach (var member in members)
            {
                if (member is PropertyDeclarationSyntax property)
                {
                    csharpClass.Properties.Add(new CsharpClass.CsharpProperty(
                        property.Identifier.ValueText, property.Type.ToString()));
                }

                if (member is NamespaceDeclarationSyntax namespaceDeclaration)
                {
                    csharpClass.Namespace = namespaceDeclaration.Name.ToString();
                }

                if (member is ClassDeclarationSyntax classDeclaration)
                {
                    csharpClass.Name = classDeclaration.Identifier.ValueText;

                    csharpClass.BaseClasses = GetBaseClasses(classDeclaration).ToList();
                }

                //if (member is MethodDeclarationSyntax method)
                //{
                //    Console.WriteLine("Method: " + method.Identifier.ValueText);
                //}
            }

            return csharpClass;
        }

        private static IEnumerable<string> GetBaseClasses(ClassDeclarationSyntax classDeclaration)
        {
            if (classDeclaration == null)
            {
                return null;
            }

            if (classDeclaration.BaseList == null)
            {
                return null;
            }

            return classDeclaration.BaseList.Types.Select(x => x.Type.ToString());
        }
    }
}

使用方法:

const string content = @"
namespace Acme.Airlines.AirCraft
{
    public class AirCraft
    {
        public virtual string Name { get; set; }

        public virtual int Code { get; set; }

        public AirCraft()
        {

        }
    }
}";

var csharpClass = CsharpClassParser.Parse(content);

Console.WriteLine(csharpClass.Name);
Console.WriteLine(csharpClass.Namespace);
Console.WriteLine(csharpClass.Properties.Count);
Console.WriteLine(csharpClass.BaseClasses.Count);

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接