如何在C#代码中编写编译器指令?能否自定义编译校验属性?
[Obsolete]? Absolutely! This kind of custom compile-time enforcement is totally achievable in .NET, and the tool you’ll want to use is Roslyn Analyzers—the same underlying technology that makes built-in attributes like [Obsolete] trigger compiler errors or warnings.
How it works
The [Obsolete] attribute is recognized natively by the C#/VB compilers, but for your specific requirement (enforcing that classes marked with [ControllingAttribute] have at least one method with [NecessaryMethod]), you’ll build a custom analyzer that runs during compilation to validate this rule.
Step-by-step implementation
Here’s a high-level breakdown of how to build this:
Define your custom attributes
First, create the two attributes you mentioned—these are just regular .NET attributes, no special compiler hooks needed yet:[AttributeUsage(AttributeTargets.Class, Inherited = false)] public class ControllingAttribute : Attribute { } [AttributeUsage(AttributeTargets.Method)] public class NecessaryMethodAttribute : Attribute { }Create a Roslyn Analyzer project
In Visual Studio, you can use the "Analyzer with Code Fix (.NET Standard)" template to start. This project will contain your analysis logic.Write the analysis logic
Your analyzer will:- Scan all classes in the codebase
- Check if a class has the
[ControllingAttribute]applied - For those classes, verify if at least one method has the
[NecessaryMethodAttribute] - If no such method exists, report a compiler error (which will block compilation, just like
[Obsolete("", true)])
Here’s a simplified snippet of the analyzer logic:
[DiagnosticAnalyzer(LanguageNames.CSharp)] public class ControllingClassAnalyzer : DiagnosticAnalyzer { public const string DiagnosticId = "ControllingClass_RequiresNecessaryMethod"; private static readonly LocalizableString Title = "Class requires a necessary method"; private static readonly LocalizableString MessageFormat = "Class '{0}' is marked with [ControllingAttribute] but has no methods marked with [NecessaryMethod]"; private static readonly LocalizableString Description = "Classes marked with [ControllingAttribute] must contain at least one method marked with [NecessaryMethod]."; 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 => ImmutableArray.Create(Rule); public override void Initialize(AnalysisContext context) { context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.EnableConcurrentExecution(); context.RegisterSymbolAction(AnalyzeSymbol, SymbolKind.NamedType); } private void AnalyzeSymbol(SymbolAnalysisContext context) { var namedTypeSymbol = (INamedTypeSymbol)context.Symbol; // Check if the class has ControllingAttribute if (!namedTypeSymbol.GetAttributes().Any(attr => attr.AttributeClass.Name == nameof(ControllingAttribute))) return; // Check if any method has NecessaryMethodAttribute var hasNecessaryMethod = namedTypeSymbol.GetMembers() .OfType<IMethodSymbol>() .Any(method => method.GetAttributes().Any(attr => attr.AttributeClass.Name == nameof(NecessaryMethodAttribute))); if (!hasNecessaryMethod) { var diagnostic = Diagnostic.Create(Rule, namedTypeSymbol.Locations[0], namedTypeSymbol.Name); context.ReportDiagnostic(diagnostic); } } }Test and deploy
- Use the included unit test project to verify your analyzer works as expected (e.g., flag invalid classes, pass valid ones)
- Package the analyzer into a NuGet package, then reference it in your target projects. Once referenced, the compiler will run your analyzer during every build and block compilation if the rule is violated.
Alternatives (for older frameworks)
If you’re working with .NET Framework (pre-.NET Core/.NET 5+), you could also use FxCop (or legacy Code Analysis) to create similar rules, but Roslyn Analyzers are the modern, cross-platform recommended approach—they integrate directly with the compiler and work in all modern .NET environments.
内容的提问来源于stack exchange,提问作者IgorZhurbenko

