c# – 可靠地将类型符号(ITypeSymbol)与Roslyn进行比较
我试图在以下情况下可靠地比较ITypeSymbol的两个实例是最容易和最直接的方式(我在一个更大的项目中遇到了这些问题,并试图尽可能地简化它):
我有这个SyntaxTree的CSharpCompilation: namespace MyAssembly { public class Foo { public Foo(Foo x) { } } } 我们使用CSharpSyntaxRewriter遍历树,更改类并更新编译.在第一次运行中,我们记住了第一个构造函数参数的ITypeSymbol(在这种情况下,它是类本身的类型). 我的第一个比较方法是调用ITypeSymbol.Equals()方法,但它返回false.它基本上返回false,因为我们更改了编译并同时获得了一个新的SemanticModel.如果我们不这样做,Equals()方法实际上返回true. 比较DeclaringSyntaxReferences(如此处所述How to compare type symbols (ITypeSymbol) from different projects in Roslyn?)返回false,因为我们同时更改了类Foo本身.如果构造函数参数的类型为Bar并且我们重写了Bar,则行为将是相同的.要验证这一点,只需取消注释该行 //RewriteBar(rewriter,compilation,resultTree); 并在代码示例中用Bar替换构造函数参数类型. 结论: 所以我的问题是: >在描述的情况下比较类型的预期方法是什么? 这是完整的测试程序,使用该程序可以重现该问题.只需复制,包括Roslyn引用并执行: using System; using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace Demo.TypeSymbol { class Program { static void Main(string[] args) { var compilation = (CSharpCompilation) GetTestCompilation(); var rewriter = new Rewriter(changeSomething: true); var tree = compilation.SyntaxTrees.First(); //first SyntaxTree is the one of class MyAssembly.Foo rewriter.Model = compilation.GetSemanticModel (tree); //first rewrite run var resultTree = rewriter.Visit (tree.GetRoot()).SyntaxTree; compilation = UpdateIfNecessary (compilation,rewriter,tree,resultTree); rewriter.Model = compilation.GetSemanticModel (resultTree); //just for demonstration; comment in to test behaviour when we are rewriting the class Bar -> in this case use Bar as constructor parameter in Foo //RewriteBar(rewriter,resultTree); //second rewrite run rewriter.Visit (resultTree.GetRoot()); //now we want to compare the types... Console.WriteLine(rewriter.ParameterTypeFirstRun); Console.WriteLine(rewriter.ParameterTypeSecondRun); //=> types are *not* equal var typesAreEqual = rewriter.ParameterTypeFirstRun.Equals (rewriter.ParameterTypeSecondRun); Console.WriteLine("typesAreEqual: " + typesAreEqual); //=> syntax references are not equal if(rewriter.ParameterTypeFirstRun.DeclaringSyntaxReferences.Any()) { var syntaxReferencesAreEqual = rewriter.ParameterTypeFirstRun.DeclaringSyntaxReferences.First() .Equals(rewriter.ParameterTypeSecondRun.DeclaringSyntaxReferences.First()); Console.WriteLine("syntaxReferencesAreEqual: " + syntaxReferencesAreEqual); } //==> other options?? } private static CSharpCompilation UpdateIfNecessary(CSharpCompilation compilation,Rewriter rewriter,SyntaxTree oldTree,SyntaxTree newTree) { if (oldTree != newTree) { //update compilation as the syntaxTree changed compilation = compilation.ReplaceSyntaxTree(oldTree,newTree); rewriter.Model = compilation.GetSemanticModel(newTree); } return compilation; } /// <summary> /// rewrites the SyntaxTree of the class Bar,updates the compilation as well as the semantic model of the passed rewriter /// </summary> private static void RewriteBar(Rewriter rewriter,CSharpCompilation compilation,SyntaxTree firstSyntaxTree) { var otherRewriter = new Rewriter(true); var otherTree = compilation.SyntaxTrees.Last(); otherRewriter.Model = compilation.GetSemanticModel(otherTree); var otherResultTree = otherRewriter.Visit(otherTree.GetRoot()).SyntaxTree; compilation = UpdateIfNecessary(compilation,otherRewriter,otherTree,otherResultTree); rewriter.Model = compilation.GetSemanticModel(firstSyntaxTree); } public class Rewriter : CSharpSyntaxRewriter { public SemanticModel Model { get; set; } private bool _firstRun = true; private bool _changeSomething; public ITypeSymbol ParameterTypeFirstRun { get; set; } public ITypeSymbol ParameterTypeSecondRun { get; set; } public Rewriter (bool changeSomething) { _changeSomething = changeSomething; } public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node) { node = (ClassDeclarationSyntax)base.VisitClassDeclaration(node); //remember the types of the parameter if (_firstRun) ParameterTypeFirstRun = GetTypeSymbol (node); else ParameterTypeSecondRun = GetTypeSymbol (node); _firstRun = false; //change something and return updated node if(_changeSomething) node = node.WithMembers(node.Members.Add(GetMethod())); return node; } /// <summary> /// Gets the type of the first parameter of the first method /// </summary> private ITypeSymbol GetTypeSymbol(ClassDeclarationSyntax classDeclaration) { var members = classDeclaration.Members; var methodSymbol = (IMethodSymbol) Model.GetDeclaredSymbol(members[0]); return methodSymbol.Parameters[0].Type; } private MethodDeclarationSyntax GetMethod() { return (MethodDeclarationSyntax) CSharpSyntaxTree.ParseText (@"public void SomeMethod(){ }").GetRoot().ChildNodes().First(); } } private static SyntaxTree[] GetTrees() { var treeList = new List<SyntaxTree>(); treeList.Add(CSharpSyntaxTree.ParseText(Source.Foo)); treeList.Add(CSharpSyntaxTree.ParseText(Source.Bar)); return treeList.ToArray(); } private static Compilation GetTestCompilation() { var mscorlib = MetadataReference.CreateFromFile(typeof(object).Assembly.Location); var refs = new List<PortableExecutableReference> { mscorlib }; // I used this to test it with a reference to an external assembly // var testAssembly = MetadataReference.CreateFromFile(@"../../../Demo.TypeSymbol.TestAssembly/bin/Debug/Demo.TypeSymbol.TestAssembly.dll"); // refs.Add (testAssembly); return CSharpCompilation.Create("dummyAssembly",GetTrees(),refs); } } public static class Source { public static string Foo => @" // for test with external assembly //using Demo.TypeSymbol.TestAssembly; namespace MyAssembly { public class Foo { public Foo(Foo x) { } } } "; public static string Bar => @" namespace MyAssembly { public class Bar { public Bar(int i) { } } } "; } } 解决方法
一种可能性是调用
SymbolFinder.FindSimilarSymbols,它将在您的新解决方案中为您提供符号名称和其他一些属性的符号.从那里你可以在你的新编译中等于.
(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |