2015-10-02 45 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