2016-12-21 85 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