Използвайки Roslyn за C#, как да получа списък с всички свойства, които съставляват върнат тип?

Да кажем, че съм запитал един метод от колекция от методи:

var myMethod = someListofMethods.FirstOrDefault(m => m.Identifier.ValueText == myMethodName);

Сега искам да взема връщания тип на метода. . .

var returnType = myMethod.ReturnType;

. . .и определя (ако не е примитивен) какви свойства се съдържат в този тип.

Така че, например, нека кажем, че върнатият тип е FooObject, който е дефиниран:

public class FooObject{
     public string Fizz {get; set; }
     public string Buzz {get; set; }
}

Как правилно да разпитам FooObject за списък с неговите свойства?

Ето какво вече опитах:

returnType.DescendantNodes().OfType<PropertyDeclarationSyntax>();

Но това не проработи. Благодаря предварително.


person Matt Cashatt    schedule 23.01.2014    source източник


Отговори (2)


Разглеждате нивото на кода на дървото на абстрактния синтаксис (AST). Следователно ред:

returnType.DescendantNodes().OfType<PropertyDeclarationSyntax>();

не връща нищо. returnType в този контекст е IdentifierNameSyntax възел на AST, съдържащ само текст FooObject. Ако искате да анализирате типа връщане, трябва:

  • интерпретира синтактичното дърво от returnType гледна точка, за да намери пълно пространство от имена на върнатия тип
  • потърсете в кода, за да намерите тази декларация за тип
  • анализирайте синтаксисното дърво на декларацията на типа, за да намерите всички негови свойства

Но всъщност това прави компилаторът, така че можете да преминете нагоре с използването на Roslyn до нивото на компилация, например:

var workspace = Workspace.LoadSolution(solutionName);
var solution = workspace.CurrentSolution;

var createCommandList = new List<ISymbol>();
var @class = solution.Projects.Select(s => s.GetCompilation()
                                            .GetTypeByMetadataName(className))
                              .FirstOrDefault();
var method = @class.GetMembers(methodName)
                    .AsList()
                    .Where(s => s.Kind == CommonSymbolKind.Method)
                    .Cast<MethodSymbol>()
                    .FirstOrDefault();
var returnType = method.ReturnType as TypeSymbol;
var returnTypeProperties = returnType.GetMembers()
                                     .AsList()
                                     .Where(s => s.Kind == SymbolKind.Property)
                                     .Select(s => s.Name);
person Konrad Kokosa    schedule 23.01.2014
comment
Работи прекрасно! Благодаря ти. - person Matt Cashatt; 23.01.2014

Създайте класа по-долу CsharpClass.cs:

public class CsharpClass
{
    public string Name { get; set; }

    public string Namespace { get; set; }

    public List<CsharpProperty> Properties { get; set; }

    public string PrimaryKeyType { 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>();
    }
}

Създайте помощния клас по-долу CsharpClassParser.cs:

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

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

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

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

                cls.PrimaryKeyType = FindPrimaryKeyType(classDeclaration);
            }

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


        return cls;
    }

    private static string FindPrimaryKeyType(ClassDeclarationSyntax classDeclaration)
    {
        if (classDeclaration == null)
        {
            return null;
        }

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

        foreach (var baseClass in classDeclaration.BaseList.Types)
        {
            var match = Regex.Match(baseClass.Type.ToString(), @"<(.*?)>");
            if (match.Success)
            {
                var primaryKey = match.Groups[1].Value;

                if (AppConsts.PrimaryKeyTypes.Any(x => x.Value == primaryKey))
                {
                    return primaryKey;
                }
            }
        }

        return null;
    }
}

Как се използва?

Прочетете съдържанието на класа като низ и го предайте на CsharpClassParser.Parse()

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);
person Alper Ebicoglu    schedule 28.04.2020
comment
Имах нужда от това, благодаря! - person Agustinus Theodorus; 16.06.2021