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

Iは、Logger.Logが同じ数を有することを必要とするいくつかの方法を持っているしたいですそれを呼び出すメソッドとしてのパラメータ

私は実行時にこれを行う方法を知っています(呼び出し側のパラメータリストを取得し、実際に受け取ったパラメータと比較するためにリフレクションを使用するだけです)。しかし、これは本当に悪い解決策です小切手の大部分は不要となる。 (それは、あなたが間違って書いたことを実行時まで知っていなかったことを意味し、その特定のメソッドを実行した場合にのみ)。

残念ながら私たちはFxCopを使用していません(または私は何らかのルールを書いています)。私はその事実を変更することに成功しないだろうと思っています。ある種のコンパイラプラグインを書くのに手間がかかりますが、人々にこのメソッドを正しく使用させる方法がありますか?

+1

ロングショット、と私はそれを自分自身を使用していないが、これはhttps://blogs.msdn.microsoft.com/hkamel/2013/10/24/visual-studio-2013-static-を助けるん深さのコード分析 - 時代遅れ/いつ?/?組み込み静的解析ツールのカスタムルールを作成することができます。 – itsme86

+1

ロギングコードを各方法に入れることは、スケーラビリティとメンテナンス性に優れた設計ではないようです。 – Agalo

+2

私はロギングのために、すべてのメソッドにロギングを追加することを忘れたり、パラメータリストが変更されたときにパラメータリストを更新することを忘れたりする必要がないAOPソリューションを探していると思います。 –

答えて

3

新しいRoslyn APIを使用してこれを達成できるはずです。

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

一度あなたが新しいプロジェクトに移動し、拡張性に移動しなければならないインストールされ、コードの修正(NuGet + VSIX)テンプレートでプロジェクトの種類の分析が表示されます:あなたはここにSDKをインストールすることをお勧めします。私はコンパイルエラーを表示するために使用するサンプルプロジェクトを作成しました:

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 
     } 
    } 
} 

私は解析のために別のプロジェクトを作成し、ここにアナライザクラスのコードです:Analyzerでいる間

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; 
     } 
    } 
} 

Code Fixプロジェクトでは、.Vsixプロジェクトがスタートアッププロジェクトである限り、F5キーを押して別のVSインスタンスを開き、アナライザをテストするプロジェクトを選択することができます。ここで

は結果である:

enter image description here

それはまたあなたの代わりにVS拡張のNuGetパッケージとしてこれをインストールする必要がありますように、何らかの理由でVSの拡張機能は、ビルドに影響を与えることはありません見て、ここで見るより完全例えば

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

あなただけの警告が表示されます

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

関連する問題