使用 Rosyln 将开关块重写为 if/else
Use Rosyln to rewrite switch blocks to if/else
我是 roslyn 的新手,所以我正在寻找一些指导或示例代码来开始做我想做的事。
我有很多类似的代码(是用工具生成的)
switch (boolVariable)
{
case false:
{
str = "blahblah";
break;
}
case true:
{
str = "somethingelse";
break;
}
default:
{
str = "ughthiswouldnevergethit";
break;
}
}
我想做的是检测语法,然后将其重写为更高效且对开发人员更友好的语法
if(boolVariable)
{
str = "somethingelse";
}
else
{
str = "blahblah";
}
本质上,我想优化所有布尔值上的开关。
你必须使用 CSharpSyntaxRewriter,下面是实现它的代码(对于简单的情况,你可以从它扩展)
(您应该下载 Syntax Visualizer extension or go to a simple online version CSharp SyntaxTree 以了解有关语法树的更多信息)
string code = ReadText("Case6.cs"); // get source code as string for a class
var tree = CSharpSyntaxTree.ParseText(code);
CompilationUnitSyntax root = (CompilationUnitSyntax)tree.GetRoot();
Compilation compilation = CSharpCompilation.Create("assembly", syntaxTrees: new[] { tree }, references: new[]
{
mscorlib = MetadataReference.CreateFromAssembly(typeof(object).Assembly),
MetadataReference.CreateFromAssembly(typeof(Stack<>).Assembly)
});
var semanticModel = compilation.GetSemanticModel(tree);
var myRewriter = new MyRewriter(semanticModel);
var result = myRewriter.Visit(tree.GetRoot());
var str = result.ToString();
// below are classes for Rewriter
public class MyRewriter : CSharpSyntaxRewriter
{
private SemanticModel semanticModel;
public MyRewriter(SemanticModel semanticModel)
{
this.semanticModel = semanticModel;
}
public override SyntaxNode VisitSwitchStatement(SwitchStatementSyntax node)
{
var result = base.VisitSwitchStatement(node);
// detect your case:
// first check if the expression in switch is bool type?
var typeInfo = semanticModel.GetTypeInfo(node.Expression);
if(typeInfo.Type.SpecialType != SpecialType.System_Boolean)
{
return result;
}
// okie we make the conversion here
// find true statement
var trueSection = node.Sections
.First(f => f.Labels.First().ToString().Contains("true"));
var falseSection = node.Sections
.First(f => f.Labels.First().ToString().Contains("false"));
var trueStatement = trueSection.Statements.Count == 1
? trueSection.Statements.First()
: SyntaxFactory.Block(trueSection.Statements);
var falseStatement = falseSection.Statements.Count == 1
? falseSection.Statements.First()
: SyntaxFactory.Block(falseSection.Statements);
var ifStatement = SyntaxFactory.IfStatement(node.Expression,
trueStatement,
SyntaxFactory.ElseClause(falseStatement));
//NOTE that: this class will remove all break statements
// it will not be correct if break in loop, you can base on that to
// write more accurately
var breakRemover = new BreakRemover();
result = breakRemover.Visit(ifStatement);
return result;
}
}
public class BreakRemover : CSharpSyntaxRewriter
{
public override SyntaxNode VisitBreakStatement(BreakStatementSyntax node)
{
// should add further check to make sure break statement is in
// switch, the idea is find closest ancestor must be switch (not
// for, foreach, while, dowhile)
return SyntaxFactory.EmptyStatement();
}
}
我是 roslyn 的新手,所以我正在寻找一些指导或示例代码来开始做我想做的事。
我有很多类似的代码(是用工具生成的)
switch (boolVariable)
{
case false:
{
str = "blahblah";
break;
}
case true:
{
str = "somethingelse";
break;
}
default:
{
str = "ughthiswouldnevergethit";
break;
}
}
我想做的是检测语法,然后将其重写为更高效且对开发人员更友好的语法
if(boolVariable)
{
str = "somethingelse";
}
else
{
str = "blahblah";
}
本质上,我想优化所有布尔值上的开关。
你必须使用 CSharpSyntaxRewriter,下面是实现它的代码(对于简单的情况,你可以从它扩展)
(您应该下载 Syntax Visualizer extension or go to a simple online version CSharp SyntaxTree 以了解有关语法树的更多信息)
string code = ReadText("Case6.cs"); // get source code as string for a class
var tree = CSharpSyntaxTree.ParseText(code);
CompilationUnitSyntax root = (CompilationUnitSyntax)tree.GetRoot();
Compilation compilation = CSharpCompilation.Create("assembly", syntaxTrees: new[] { tree }, references: new[]
{
mscorlib = MetadataReference.CreateFromAssembly(typeof(object).Assembly),
MetadataReference.CreateFromAssembly(typeof(Stack<>).Assembly)
});
var semanticModel = compilation.GetSemanticModel(tree);
var myRewriter = new MyRewriter(semanticModel);
var result = myRewriter.Visit(tree.GetRoot());
var str = result.ToString();
// below are classes for Rewriter
public class MyRewriter : CSharpSyntaxRewriter
{
private SemanticModel semanticModel;
public MyRewriter(SemanticModel semanticModel)
{
this.semanticModel = semanticModel;
}
public override SyntaxNode VisitSwitchStatement(SwitchStatementSyntax node)
{
var result = base.VisitSwitchStatement(node);
// detect your case:
// first check if the expression in switch is bool type?
var typeInfo = semanticModel.GetTypeInfo(node.Expression);
if(typeInfo.Type.SpecialType != SpecialType.System_Boolean)
{
return result;
}
// okie we make the conversion here
// find true statement
var trueSection = node.Sections
.First(f => f.Labels.First().ToString().Contains("true"));
var falseSection = node.Sections
.First(f => f.Labels.First().ToString().Contains("false"));
var trueStatement = trueSection.Statements.Count == 1
? trueSection.Statements.First()
: SyntaxFactory.Block(trueSection.Statements);
var falseStatement = falseSection.Statements.Count == 1
? falseSection.Statements.First()
: SyntaxFactory.Block(falseSection.Statements);
var ifStatement = SyntaxFactory.IfStatement(node.Expression,
trueStatement,
SyntaxFactory.ElseClause(falseStatement));
//NOTE that: this class will remove all break statements
// it will not be correct if break in loop, you can base on that to
// write more accurately
var breakRemover = new BreakRemover();
result = breakRemover.Visit(ifStatement);
return result;
}
}
public class BreakRemover : CSharpSyntaxRewriter
{
public override SyntaxNode VisitBreakStatement(BreakStatementSyntax node)
{
// should add further check to make sure break statement is in
// switch, the idea is find closest ancestor must be switch (not
// for, foreach, while, dowhile)
return SyntaxFactory.EmptyStatement();
}
}