我刚开始使用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"))))
答案 0 :(得分:6)
好吧,我会使用语义而不是语法来解决这个问题。像(在我头顶)的东西:
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));
答案 1 :(得分:3)
我的类似任务的方法(我想重写用特定属性修饰的方法和属性)是找到属性符号的所有用法,然后迭代引用并获取方法/属性声明语法:
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);
}
}
}
}
答案 2 :(得分:0)
这是我的助手类,它查找所有属性,类名和类名称空间
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);