使用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