我刚刚开始使用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);