使用PostSharp进行面向方面的编程将允许你创建拦截器,这些拦截器在像方法调用和属性访问这样定义明确的点上挂钩到你的代码中。这是通过重写源代码生成的IL来完成的。
在你的情况下,你正在寻找具体if
陈述,但他们不容易识别在生成的IL。以下是您的示例方法生成的内容:
nop
ldarg.1
ldstr "somedumbstringbecausethisishorriblewayofdoingthings"
call System.String.op_Equality
stloc.0
ldloc.0
brfalse.s NotEqual
nop
**True branch**
br.s Return
NotEqual: nop
**False branch**
Return: ret
但是,这是非优化版本。优化编译器生成的代码:
ldarg.1
ldstr "somedumbstringbecausethisishorriblewayofdoingthings"
call System.String.op_Equality
brfalse.s NotEqual
**True branch**
ret
NotEqual:
**False branch**
ret
所以,如果你想要做的IL重写你可以想象拦截方法调用,寻找一个IL签名,看起来像ldstr, "...", call System.String.op_Equality, brfalse.s
,然后重写IL修改代码。但是,这种方法非常脆弱。源代码中的小差异可能会产生具有不同签名的IL,只有编译器编写者才会真正知道您所查找的IL签名的整个范围。如果条件语句涉及多个与逻辑运算符相结合的术语,那么确定插入代码代码的位置会变得非常复杂。然后有误报。就我个人而言,我不相信这是一个可行的用于检测代码的策略。
但是,您有另一种选择。您可以使用Roslyn编译源代码并使用Roslyn重写器来修改原始源代码树。这是源代码级别的AOP,而不是在IL级别提供AOP的PostSharp。
要重写语法树,您需要从CSharpSyntaxRewriter
派生类。此重写将修改if
语句所以VisitIfStatement
方法被覆盖:
class IfStatementRewriter : CSharpSyntaxRewriter {
public override SyntaxNode VisitIfStatement(IfStatementSyntax ifStatement) {
var containsMagicString = ifStatement
.Condition
.DescendantNodes()
.Any(
syntaxNode => syntaxNode.ToString() == @"""somedumbstringbecausethisishorriblewayofdoingthings"""
);
if (!containsMagicString)
// Do not modify if statement.
return ifStatement;
// Only look at the "true" part and assume it is a block (has curly braces).
var block = ifStatement.Statement as BlockSyntax;
if (block == null)
// Do not modify if statement.
return ifStatement;
// Insert instrumentation code at start of block.
var instrumentationStatements = CreateInstrumentationStatements("True branch");
var modifiedStatements = block.Statements.InsertRange(0, instrumentationStatements);
var modifiedBlock = block.WithStatements(modifiedStatements);
return ifStatement.WithStatement(modifiedBlock);
}
}
插入仪器代码System.Console.WriteLine("True branch");
以下,而多毛的方法是使用:
IEnumerable<StatementSyntax> CreateInstrumentationStatements(String text) {
return SyntaxFactory.SingletonList<StatementSyntax>(
SyntaxFactory.ExpressionStatement(
SyntaxFactory.InvocationExpression(
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.MemberAccessExpression(
SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName("System"),
SyntaxFactory.IdentifierName("Console")
)
.WithOperatorToken(SyntaxFactory.Token(SyntaxKind.DotToken)),
SyntaxFactory.IdentifierName("WriteLine")
)
.WithOperatorToken(SyntaxFactory.Token(SyntaxKind.DotToken))
)
.WithArgumentList(
SyntaxFactory.ArgumentList(
SyntaxFactory.SeparatedList(
new[] {
SyntaxFactory.Argument(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Literal(text)
)
)
}
)
)
)
)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken))
);
}
现在,您可以修改语法树:
var root = (CompilationUnitSyntax) syntaxTree.GetRoot();
var rewriter = new IfStatementRewriter();
var rewrittenRoot = rewriter.Visit(root);
您将需要大幅扩展代码以更好地过滤if
陈述,并处理所有不同的方式if
陈述可以被写入,但相比做IL重写这应该会给你一个更高的成功几率。
我知道。这是一个关键字......因此,我不能覆盖它的功能 – MegaMark