使用 Roslyn 转换流式表达式链
Transforming chain of fluent expressions with Roslyn
我有以下示例 classes:
// 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
class 的代码并将 FluentSample.Build()
替换为 Fluent.FluentSample.Build().WithOption("addnewline")
。为了做到这一点,我需要确保它真的在调用 class(而不是另一个同名的),这需要一些符号绑定。
从语法可视化工具 window 中,我发现它基本上是一个 InvocationExpressionSyntax
节点,所以我从 CSharpSyntaxRewriter
覆盖 VisitInvocationExpression
:
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
。如何检查这是否真的是对 Fluent.FluentSample
的调用并应用转换?
原来的代码几乎就是这样,所以语句总是流畅的。
这应该让你开始:
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"))))));
}
}
我有以下示例 classes:
// 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
class 的代码并将 FluentSample.Build()
替换为 Fluent.FluentSample.Build().WithOption("addnewline")
。为了做到这一点,我需要确保它真的在调用 class(而不是另一个同名的),这需要一些符号绑定。
从语法可视化工具 window 中,我发现它基本上是一个 InvocationExpressionSyntax
节点,所以我从 CSharpSyntaxRewriter
覆盖 VisitInvocationExpression
:
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
。如何检查这是否真的是对 Fluent.FluentSample
的调用并应用转换?
原来的代码几乎就是这样,所以语句总是流畅的。
这应该让你开始:
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"))))));
}
}