Как с помощью 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