将以下分析器代码编译为DLL后,将DLL添加到项目分析器列表中,即可进行相关分析。
分析器代码:
using System;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics;
namespace DontCallBaseImplementation
{
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class DontCallBaseImplementationAttribute : Attribute { }
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class DontCallBaseImplementationAnalyzer : DiagnosticAnalyzer
{
public const string DiagnosticId = "DontCallBaseImplementation";
private static readonly LocalizableString Title = "Invalid base method implementation call";
private static readonly LocalizableString MessageFormat = "Method '{0}' with [DontCallBaseImplementationAttribute] should not call base method implementation";
private static readonly LocalizableString Description = "The method in the derived class calls the base method implementation which is marked with [DontCallBaseImplementationAttribute].";
private const string Category = "Usage";
private static readonly 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.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();
context.RegisterSyntaxNodeAction(AnalyzeSyntax, SyntaxKind.InvocationExpression);
}
private static void AnalyzeSyntax(SyntaxNodeAnalysisContext context)
{
InvocationExpressionSyntax invocationExpression = (InvocationExpressionSyntax)context.Node;
if (invocationExpression.Expression is MemberAccessExpressionSyntax memberAccess &&
memberAccess.Expression is BaseExpressionSyntax)
{
IMethodSymbol invokedSymbol = context.SemanticModel.GetSymbolInfo(invocationExpression).Symbol as IMethodSymbol;
if (invokedSymbol == null) return;
IMethodSymbol currentMethod = context.ContainingSymbol as IMethodSymbol;
IMethodSymbol baseMethod = currentMethod?.OverriddenMethod;
if (!SymbolEqualityComparer.Default.Equals(invokedSymbol, baseMethod)) return;
AttributeData dontCallBaseImplAttr = baseMethod.GetAttributes()
.FirstOrDefault(attr => attr.AttributeClass.Name == nameof(DontCallBaseImplementationAttribute));
if (dontCallBaseImplAttr != null)
{
Diagnostic diagnostic = Diagnostic.Create(Rule, invocationExpression.GetLocation(), memberAccess.Name);
context.ReportDiagnostic(diagnostic);
}
}
}
}
}
添加分析器:
在项目的配置文件(csproj)中,添加 ItemGroup
指定分析器引用:
< !--Add to YOUR_PROJECT.csproj-- >
< Project Sdk = "Microsoft.NET.Sdk" >
< !--... -->
< ItemGroup >
< Analyzer Include = "ABSOLUTE_OR_RELATIVE_FOLDER\DontCallBaseImplementation.dll" />
</ ItemGroup >
< !--... -->
</ Project >
代码中的使用方法:
定义一个 DontCallBaseImplementationAttribute
类(也可以直接使用分析器DLL中提供的同名类,但要为项目添加对DLL的依赖项):
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
public class DontCallBaseImplementationAttribute : Attribute { }
在不希望子类覆写时调用父类方法实现的虚方法上,添加上述特性:
class BaseClass
{
[DontCallBaseImplementation]
public virtual void DoSomething()
{
Console.WriteLine("BaseClass.DoSomething()");
}
}
之后,若子类重写方法时调用了父类方法实现,编辑器会报告错误:
class DerivedClass : BaseClass
{
public override void DoSomething()
{
base.DoSomething(); // Will report an error
}
}