2015-10-02 84 views
2

我在尋找的行爲會像下面這樣:有無PostSharp幫助登錄IF語句

[IfElseLogging] 
public void Foo(string bar) 
{ 
    if (bar == "somedumbstringbecausethisishorriblewayofdoingthings") 
    { 
     // here the aspect would log that the if statement above was true, 
     // and thus took this path 
    } 
    else 
    { 
     // here the aspect would log that the if statement above was false, 
     // and thus took this path 
    } 
} 

如果有一種方法劫持if功能,我將能夠與它裝飾Postsharp使用的日誌記錄和我的手中的灰塵。我在這個問題上增加的另一個原因是,我不確定我是否清楚自己在問什麼。

這是由一個非常小的開發人員完成的項目。我的任務不僅是重構,而且要記錄代碼對流程圖級別的作用。由於代碼,缺乏描述的,不良的技術和做法,項目性質等的性質......這裏是我與掙扎的事情:

  • 我不能簡單地調試和步本地代碼
  • 我無法創建單元測試並安全地重構
  • 我無法理解此代碼的作用!
  • 我沒有什麼獨到的需求文檔,瞭解的功能的目標是什麼

所以我希望用Postsharp基本上給我的代碼路徑和單元測試的標準,因爲我不知道該怎麼別人來得到它。

在另一個想法上,是否有可能在發現if語句時觸發事件的某個方面?

+0

我知道。這是一個關鍵字......因此,我不能覆蓋它的功能 – MegaMark

回答

2

使用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重寫這應該會給你一個更高的成功機率。

+0

謝謝你的解釋,這正是我一直在尋找的! – MegaMark

0

您應該繼承OnMethodBoundaryAspect並覆蓋OnEntry(MethodExecutionArgs args)OnExceptionAspect並覆蓋OnException(MethodExecutionArgs args)。你可以得到並比較你的string bar來自參數

+0

這不是我正在尋找的,這很可能是我問題的糟糕措辭是這個問題......我需要的是記錄什麼路徑代碼通過'if'語句。這個例子過於簡單。我正在處理由非常小的開發人員編寫的遺留代碼,我需要知道它是如何工作的,並且由於其複雜的性質以及幻數和其他比較類似的錯誤編碼實踐以及處理不存在能夠在本地調試代碼...並且必須爲客戶記錄代碼的實際功能......我正在查找日誌 – MegaMark

+0

這可以表示代碼所需的路徑......如果我有更多信息,我會創建單元測試並從那裏重構......但考慮到這種情況,這並不是很可行。 – MegaMark