问题
I am just starting with Roslyn, and I want to find all properties that are annotated with attribute names "OneToOne". I fired up SyntaxVisualizer and was able to get reference to that node, but I am wondering is there a simpler way to achieve this. This is what I have:
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"))))
回答1:
Well, I would go about this using Semantics, not Syntax. Something like (off the top of my head):
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));
回答2:
My approach for similar task (I wanted to rewrite methods and properties decorated with specific attribute) was to find all usages of the attribute symbol and then iterate over references and get method/property declaration syntax:
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>();
}
}
I had to write few extension methods to make life easier:
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);
}
}
}
}
来源:https://stackoverflow.com/questions/19594847/how-to-get-all-properties-that-are-anotated-with-some-attribute