gpt4 book ai didi

c# - 可靠地将类型符号 (ITypeSymbol) 与 Roslyn 进行比较

转载 作者:太空狗 更新时间:2023-10-29 20:30:14 25 4
gpt4 key购买 nike

我试图可靠地比较 ITypeSymbol 的两个实例,在以下情况下可能是最简单和最直接的方法(我在一个更大的项目中遇到了这些问题,并试图尽可能地简化它可能):

我有一个 CSharpCompilation 和这个 SyntaxTree:

  namespace MyAssembly
{
public class Foo
{
public Foo(Foo x)
{
}
}
}

我们正在使用 CSharpSyntaxRewriter 遍历树,更改类并更新 Compilation。在第一次运行中,我们记得第一个构造函数参数的 ITypeSymbol(在本例中是类本身的类型)。更新编译后,我们再次调用同一个重写器,并第二次从构造函数参数中获取 ITypeSymbol。之后,我比较了我希望代表相同类型 MyAssembly.Foo 的两个 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

结论:ITypeSymbol.Equals() 不适用于新的编译和语义模型,并且比较 DeclaringSyntaxReferences 不适用于我们同时更改的类型。(我还使用一种外部程序集测试了行为——在这种情况下,ITypeSymbol.Equals() 对我有用。)

所以我的问题是:

  • 在所描述的情况下比较类型的预期方法是什么?
  • 是否有一个包罗万象的解决方案,或者我必须混合/组合不同的方法来确定类型相等性(也许还采用完全限定名称的字符串表示形式考虑在内)?

这是我可以重现问题的完整测试程序。只需复制,包括 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, compilation, 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这将在您的新解决方案中为您提供一个按名称和其他一些属性匹配的符号。从那里你可以在你的新编译中使用 Equals。

关于c# - 可靠地将类型符号 (ITypeSymbol) 与 Roslyn 进行比较,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34243031/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com