2016-12-21 125 views
3

特定数目我有记录方法名(我通过反射得到)和参数(手动传递给记录器)的记录器。下面是一个正确记录日志的例子:强制的参数

public void Foo() { 
    // This is correct - the method has no parameters, neither does the logging 
    Logger.Log(); 
    // Method 
} 

public void Foo1(int a, int b) { 
    // Log both of the parameters correctly 
    Logger.Log(a, b); 
    // Rest of method 
} 

但是,人们会周期性地调用它。例如:

public void Foo1(int a, int b) { 
    // Didn't record any of the parameters 
    Logger.Log(); 
    // Rest of method 
} 

public void Foo1(int a, int b, int c) { 
    // Someone changed the number of parameters but didn't update the logging call 
    Logger.Log(a, b); 
} 

日志方法的签名是:

public void Log(params object[] parameters) 

我希望有需要的某种方式,Logger.Log具有相同数量的作为调用它的方法的参数。

我知道如何在运行时执行此操作(只需使用反射来获取调用者的参数列表,并将其与实际接收的参数进行比较),但这对我来说是一个非常糟糕的解决方案,因为我认为,大部分检查将是不必要的。 (这也意味着你直到运行时才会知道你写错了,然后只有当你碰巧执行那个特定的方法时)。

现在我们不使用的FxCop不幸的是(或者我只是写某种规则的),我怀疑,我也不会改变这一事实获得成功。在编写某种编译器插件的时候,有没有办法强制人们正确使用这种方法?

+1

远射,我还没有用过它自己,但这是否有帮助https://blogs.msdn.microsoft.com/hkamel/2013/10/24/visual-studio-2013-static-代码分析,深入的 - 什么 - 和 - 如何时/?您可以为内置的静态分析工具创建自定义规则。 – itsme86

+1

将日志记录代码放入每种方法似乎都不是很好的可扩展性和可维护性设计。 – Agalo

+2

我认为你应该寻找一些AOP解决方案来记录你不需要记住添加日志记录到每个方法或记住更新参数列表时,如果他们改变。 –

回答

3

你应该能够做到这一点使用新的API罗斯林的。你会想在这里安装SDK:

https://marketplace.visualstudio.com/items?itemName=VisualStudioProductTeam.NETCompilerPlatformSDK

一旦安装,你应该去新的项目并导航到可扩展性,你会看到代码修复(+的NuGet VSIX)模板的项目类型分析。我创建了我用来显示编译器错误的示例项目:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Threading.Tasks; 

namespace AnalyzerTest 
{ 
    public static class Logger 
    { 
     public static void Log(params object[] parameters) 
     { 

     } 
    } 
} 

namespace AnalyzerTest 
{ 
    public class Foo 
    { 
     public void Foo1(int a, int b) 
     { 
      // Didn't record any of the parameters 
      Logger.Log(); 
      // Rest of method 
     } 
    } 
} 

我创建了分析一个单独的项目,这里是对分析类的代码:尽管在分析

using System; 
using System.Collections.Immutable; 
using Microsoft.CodeAnalysis; 
using Microsoft.CodeAnalysis.CSharp; 
using Microsoft.CodeAnalysis.CSharp.Syntax; 
using Microsoft.CodeAnalysis.Diagnostics; 
using Microsoft.CodeAnalysis.Semantics; 

namespace Analyzer1 
{ 
    [DiagnosticAnalyzer(LanguageNames.CSharp)] 
    public class LoggerAnalyzer : DiagnosticAnalyzer 
    { 
     public const string DiagnosticId = "Logging"; 
     internal const string Title = "Logging error"; 
     internal const string MessageFormat = "Logging error {0}"; 
     internal const string Description = "You should have the same amount of arguments in the logger as you do in the method."; 
     internal const string Category = "Syntax"; 

     internal static DiagnosticDescriptor Rule = 
      new DiagnosticDescriptor(DiagnosticId, Title, MessageFormat, 
      Category, DiagnosticSeverity.Error, isEnabledByDefault: true, description: Description); 

     public override ImmutableArray<DiagnosticDescriptor> 
      SupportedDiagnostics 
     { get { return ImmutableArray.Create(Rule); } } 

     public override void Initialize(AnalysisContext context) 
     { 
      context.RegisterSyntaxNodeAction(
       AnalyzeNode, SyntaxKind.InvocationExpression); 
     } 

     private void AnalyzeNode(SyntaxNodeAnalysisContext context) 
     { 
      var invocationExpr = (InvocationExpressionSyntax)context.Node; 
      var memberAccessExpr = invocationExpr.Expression as MemberAccessExpressionSyntax; 

      if (memberAccessExpr != null && memberAccessExpr.Name.ToString() != "Log") 
      { 
       return; 
      } 

      var memberSymbol = 
       context.SemanticModel.GetSymbolInfo(memberAccessExpr).Symbol as IMethodSymbol; 

      if (memberSymbol == null || !memberSymbol.ToString().StartsWith("AnalyzerTest.Logger.Log")) 
      { 
       return; 
      } 

      MethodDeclarationSyntax parent = GetParentMethod(context.Node); 
      if(parent == null) 
      { 
       return; 
      } 

      var argumentList = invocationExpr.ArgumentList; 

      Int32 parentArgCount = parent.ParameterList.Parameters.Count; 
      Int32 argCount = argumentList != null ? argumentList.Arguments.Count : 0; 

      if (parentArgCount != argCount) 
      { 
       var diagnostic = Diagnostic.Create(Rule, invocationExpr.GetLocation(), Description); 
       context.ReportDiagnostic(diagnostic); 
      } 
     } 

     private MethodDeclarationSyntax GetParentMethod(SyntaxNode node) 
     { 
      var parent = node.Parent as MethodDeclarationSyntax; 
      if(parent == null) 
      { 
       return GetParentMethod(node.Parent); 
      } 

      return parent; 
     } 
    } 
} 

与代码修复项目,你可以按F5键(只要你.Vsix项目是启动项目),它会打开另一个VS实例,你可以选择你想测试分析仪项目。

下面是结果:

enter image description here

它也像你将不得不安装此作为NuGet包,而不是VS扩展,无论出于何种原因VS扩展不会影响构建和你只会得到警告:

https://stackoverflow.com/a/39657967/1721372

更完整的例子在这里看到:

https://msdn.microsoft.com/en-us/magazine/dn879356.aspx