我有以下示例类:
// Sample fluent style class
namespace Fluent
{
public class FluentSample
{
private readonly IList<string> _options;
public FluentSample()
{
_options = new List<string>();
}
public static FluentSample Build()
{
return new FluentSample();
}
public FluentSample WithOption(string option)
{
_options.Add(option);
return this;
}
}
}
// Sample class that uses the one above
public class FooSample
{
public void Build()
{
FluentSample.Build()
.WithOption("uppercase")
.WithOption("trim")
.WithOption("concat");
}
}
假设我要转换使用FluentSample
类的代码并将FluentSample.Build()
替换为Fluent.FluentSample.Build().WithOption("addnewline")
。为了做到这一点,我需要确保它真正调用该类(而不是另一个具有相同名称的类),这需要一些符号绑定。
在Syntax Visualizer窗口中,我发现它基本上是InvocationExpressionSyntax
节点,因此我从VisitInvocationExpression
覆盖CSharpSyntaxRewriter
:< / p>
public class Rewritter : CSharpSyntaxRewriter
{
private readonly SemanticModel SemanticModel;
public Rewritter(SemanticModel semanticModel)
{
SemanticModel = semanticModel;
}
public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
{
var symbol = SemanticModel.GetSymbolInfo(node).Symbol;
}
}
但是,我得到的符号是许多WithOption
次来电。语法分析表明它包含一系列表达式,这些表达式只会导致越来越多的WithOption
s。如何检查这是否真的是对Fluent.FluentSample
的调用并应用转换?
原始代码几乎与此类似,因此语句将始终流畅。
答案 0 :(得分:1)
这应该让你开始:
public class Rewritter : CSharpSyntaxRewriter
{
private readonly SemanticModel SemanticModel;
public Rewritter(SemanticModel semanticModel)
{
SemanticModel = semanticModel;
}
public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
{
var symbol = SemanticModel.GetSymbolInfo(node).Symbol as IMethodSymbol;
// symbol could be null, e.g. when invoking a delegate
if (symbol == null)
{
return base.VisitInvocationExpression(node);
}
// symbol must be called Build and have 0 parameters
if (symbol.Name != "Build" ||
symbol.Parameters.Length != 0)
{
return base.VisitInvocationExpression(node);
}
// TODO you might want to check that the parent is not an invocation of .WithOption("addnewline") already
// symbol must be a method on the type "Fluent.FluentSample"
var type = symbol.ContainingType;
if (type.Name != "FluentSample" || type.ContainingSymbol.Name != "Fluent")
{
return base.VisitInvocationExpression(node);
}
// TODO you may want to add a check that the containing symbol is a namespace, and that its containing namespace is the global namespace
// we have the right one, so return the syntax we want
return
SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
node,
SyntaxFactory.IdentifierName("WithOption")),
SyntaxFactory.ArgumentList(
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.Argument(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Literal("addnewline"))))));
}
}