能否标记即将变为抽象的.NET虚成员以触发编译器警告?
在.NET中实现标记未来抽象成员的子类警告机制
咱们都知道.NET里的ObsoleteAttribute能标记要移除的成员或类型,触发编译器警告612/618。现在需要一个类似的[SoonToBeAbstract]特性:标记当前是虚成员、未来要改成抽象的方法/属性,要求编译器给没重写该成员的子类发警告,避免以后成员变抽象后子类直接编译失败,而且不想自己手动写代码遍历继承链。
期望的效果示例:
public class MyBaseClass { [SoonToBeAbstract] // 自定义特性 public virtual void DoSomething() {} } public class MySubClass : MyBaseClass // 这里该出警告:没重写即将变抽象的DoSomething { }
实现方案
.NET原生没有内置这个检查逻辑,最靠谱的方式是用Roslyn代码分析器来做——而且Roslyn的API已经封装了继承链遍历的逻辑,不用咱们自己手动写递归或者遍历代码:
1. 先定义自定义特性
这个特性只是个标记,用来让分析器识别目标成员:
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false)] public class SoonToBeAbstractAttribute : Attribute { // 不需要额外逻辑,纯标记用 }
2. 编写Roslyn分析器核心逻辑
分析器要做的就是:找到被[SoonToBeAbstract]标记的基类虚成员,然后检查所有子类有没有重写它,没重写就发警告。
核心代码片段(已经利用Roslyn内置的继承链和成员匹配逻辑):
public override void Initialize(AnalysisContext context) { context.EnableConcurrentExecution(); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); // 注册类型分析的回调 context.RegisterSymbolAction(AnalyzeType, SymbolKind.NamedType); } private void AnalyzeType(SymbolAnalysisContext context) { var typeSymbol = (INamedTypeSymbol)context.Symbol; // 遍历当前类型的所有基类(Roslyn的GetBaseTypesAndThis帮我们处理了继承链) foreach (var baseType in typeSymbol.BaseType.GetBaseTypesAndThis()) { // 找基类里被SoonToBeAbstract标记的虚成员 foreach (var member in baseType.GetMembers()) { if (member is IMethodSymbol method && method.IsVirtual && method.GetAttributes().Any(attr => attr.AttributeClass.Name == nameof(SoonToBeAbstractAttribute))) { // 检查当前子类有没有重写这个方法 var hasOverride = typeSymbol.GetMembers(method.Name) .OfType<IMethodSymbol>() .Any(m => m.IsOverride && m.OverriddenMethod.Equals(method, SymbolEqualityComparer.Default)); if (!hasOverride) { // 发出警告 context.ReportDiagnostic(Diagnostic.Create( Descriptors.SoonToBeAbstractWarning, typeSymbol.Locations[0], typeSymbol.Name, method.Name)); } } // 如需支持属性,可添加类似的IPropertySymbol判断逻辑 } } }
3. 定义诊断规则
指定警告的ID、提示消息等:
private static class Descriptors { public static readonly DiagnosticDescriptor SoonToBeAbstractWarning = new( id: "STA001", title: "未重写即将抽象的成员", messageFormat: "类型 '{0}' 未重写基类中即将变为抽象的成员 '{1}'", category: "Design", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true, description: "该成员未来会改为抽象,子类必须重写以避免后续编译失败。"); }
说明
这个方案完全不需要手动实现继承链遍历——Roslyn的GetBaseTypesAndThis()方法已经帮我们遍历了所有基类,IsOverride和OverriddenMethod属性也直接帮我们判断了子类是否重写了目标成员,逻辑简洁且可靠。
内容的提问来源于stack exchange,提问作者F-H
相关产品推荐
相关产品推荐

