如何获取使用某些属性批注的所有属性

本文关键字:属性 何获取 获取 | 更新日期: 2023-09-27 18:36:51

我刚刚开始使用Roslyn,我想找到所有用属性名称"OneToOne"注释的属性。我启动了 SyntaxVisualizer 并能够获得对该节点的引用,但我想知道是否有更简单的方法来实现这一目标。这是我所拥有的:

var prop = document.GetSyntaxRoot()
             .DescendantNodes()
             .OfType<PropertyDeclarationSyntax>()
             .Where(p => p.DescendantNodes()
                 .OfType<AttributeSyntax>()
                 .Any(a => a.DescendantNodes()
                     .OfType<IdentifierNameSyntax>()
                     .Any(i => i.DescendantTokens()
                         .Any(dt => dt.Kind == SyntaxKind.IdentifierToken
                                 && dt.ValueText == "OneToOne"))))

如何获取使用某些属性批注的所有属性

好吧,我会使用语义而不是语法来解决这个问题。 像这样(从我的头顶上):

var attributeSymbol = compilation.GetTypeByMetadataName("ConsoleApplication1.OneToOneAttribute");
var propertySymbol = compilation.GetTypeByMetadataName("ConsoleApplication1.Program")
                     .GetMembers()
                     .Where(m => 
                            m.Kind == CommonSymbolKind.Property && 
                            m.GetAttributes().Any(a => a.AttributeClass.MetadataName == attributeSymbol.MetadataName));

我执行类似任务的方法(我想重写用特定属性装饰的方法和属性)是找到属性符号的所有用法,然后迭代引用并获取方法/属性声明语法:

var attributeSymbol = compilation.FindSymbol(typeof(<Your attribute type>));
var references = attributeSymbol.FindReferences(solution);
foreach (ReferencedSymbol referencedSymbol in references)
{
    foreach (ReferenceLocation location in referencedSymbol.Locations)
    {
        var propertyDeclaration = location.Document.GetSyntaxRoot()
            .FindToken(location.Location.SourceSpan.Start)
            .Parent
            .FirstAncestorOrSelf<PropertyDeclarationSyntax>();
    }
}

我不得不编写一些扩展方法来使生活更轻松:

public static class CompilationExtensions
{
    public static INamedTypeSymbol FindSymbol(this CommonCompilation compilation, Type searchedType)
    {
        var splitFullName = searchedType.FullName.Split('.');
        var namespaceNames = splitFullName.Take(splitFullName.Length - 1).ToArray();
        var className = splitFullName.Last();
        if (namespaceNames.Length == 0)
            return compilation.GlobalNamespace.GetAllTypes(new CancellationToken()).First(n => n.Name == className);
        var namespaces = compilation.GlobalNamespace.GetNamespaceMembers();
        INamespaceSymbol namespaceContainingType = null;
        foreach (var name in namespaceNames)
        {
            namespaceContainingType = namespaces.First(n => n.Name == name);
            namespaces = namespaceContainingType.GetNamespaceMembers();
        }
        return namespaceContainingType.GetAllTypes(new CancellationToken()).First(n => n.Name == className);
    }
}
public static class INamespaceSymbolExtension
{
    public static IEnumerable<INamedTypeSymbol> GetAllTypes(this INamespaceSymbol @namespace, CancellationToken cancellationToken)
    {
        Queue<INamespaceOrTypeSymbol> symbols = new Queue<INamespaceOrTypeSymbol>();
        symbols.Enqueue(@namespace);
        while (symbols.Count > 0)
        {
            cancellationToken.ThrowIfCancellationRequested();
            INamespaceOrTypeSymbol namespaceOrTypeSymbol = symbols.Dequeue();
            INamespaceSymbol namespaceSymbol = namespaceOrTypeSymbol as INamespaceSymbol;
            if (namespaceSymbol == null)
            {
                INamedTypeSymbol typeSymbol = (INamedTypeSymbol) namespaceOrTypeSymbol;
                Array.ForEach(typeSymbol.GetTypeMembers().ToArray(), symbols.Enqueue);
                yield return typeSymbol;
            }
            else
            {
                Array.ForEach(namespaceSymbol.GetMembers().ToArray(), symbols.Enqueue);
            }
        }
    }
}
这是我

的帮助程序类,用于查找所有属性、类名和类命名空间

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>();
    }
}
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;
    }
}

用法:

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);