如何在Incremental Source Generator中获取泛型类的泛型实参
Extract Generic Arguments from
User<T> Instantiations Using Incremental Source Generator Here's a complete implementation to capture the generic type arguments whenever your User<T> class is instantiated, using an incremental source generator:
Step 1: Implement the Generator
Create a class that implements IIncrementalGenerator, combining syntax filtering and semantic analysis to target User<T> instantiations:
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.Syntax; [Generator] public class UserGenericArgumentGenerator : IIncrementalGenerator { public void Initialize(IncrementalGeneratorInitializationContext context) { // Capture all User<T> instantiations (explicit and target-typed) var userInstances = context.SyntaxProvider .CreateSyntaxProvider( predicate: (node, _) => IsUserInstantiation(node), transform: (ctx, _) => GetGenericArgumentDetails(ctx)) .Where(details => details != null); // Process the captured details (example: emit diagnostics or generate code) context.RegisterSourceOutput(userInstances, (context, details) => { // Emit an info diagnostic showing the generic argument var diagnostic = Diagnostic.Create( new DiagnosticDescriptor( "USERGEN001", "User<T> Instantiation Detected", "User created with generic type: {0}", "UserGenerator", DiagnosticSeverity.Info, isEnabledByDefault: true), details.Location, details.GenericArgumentName); context.ReportDiagnostic(diagnostic); // Uncomment below to generate source code using the generic argument // context.AddSource( // $"UserArg_{details.Location.GetHashCode()}.cs", // GenerateTrackingCode(details.GenericArgumentName)); }); } // Check if the syntax node is a User<T> instantiation private static bool IsUserInstantiation(SyntaxNode node) { if (node is not ObjectCreationExpressionSyntax objExpr) return false; // Handle explicit type (new User<bool>()) or target-typed (new()) var typeNode = objExpr.Type ?? objExpr.Parent? .DescendantNodes() .OfType<VariableDeclaratorSyntax>() .FirstOrDefault()? .Type; return typeNode is GenericNameSyntax genericName && genericName.Identifier.Text == "User" && genericName.TypeArgumentList.Arguments.Count == 1; } // Get semantic details about the generic argument private static GenericArgumentDetails? GetGenericArgumentDetails(GeneratorSyntaxContext ctx) { var objExpr = (ObjectCreationExpressionSyntax)ctx.Node; var typeNode = objExpr.Type ?? objExpr.Parent? .DescendantNodes() .OfType<VariableDeclaratorSyntax>() .FirstOrDefault()? .Type; if (typeNode is not GenericNameSyntax genericName) return null; var semanticModel = ctx.SemanticModel; var typeSymbol = semanticModel.GetTypeInfo(genericName).Type as INamedTypeSymbol; // Verify it's actually our User<T> class if (typeSymbol == null || !typeSymbol.ConstructedFrom.Name.Equals("User")) return null; var genericArg = typeSymbol.TypeArguments[0]; return new GenericArgumentDetails( genericArg.ToDisplayString(), objExpr.GetLocation()); } private record GenericArgumentDetails(string GenericArgumentName, Location Location); // Optional: Generate code that tracks the generic argument private static string GenerateTrackingCode(string argName) { return $$""" namespace UserGeneratorOutput; public static class UserGenericArgTracker_{{argName}} { public const string TrackedGenericType = "{{argName}}"; } """; } }
How It Works
- Syntax Filtering: The predicate checks for
ObjectCreationExpressionSyntaxnodes that correspond toUser<T>—this includes both explicit instantiations (new User<bool>()) and target-typed ones (new()where the variable type isUser<SomeOtherClass>). - Semantic Analysis: We use the semantic model to resolve the actual type symbol of the generic
Userclass, ensuring we're targeting the correct type (not a different class namedUser). Then we extract the first generic argument. - Processing: In this example, we emit an info-level diagnostic to show the generic type, but you can modify the
RegisterSourceOutputaction to generate source code, collect statistics, or perform other tasks.
Testing
When you build your project with this generator enabled, every User<T> instantiation will trigger a diagnostic. For example:
User<bool> userWithBool = new();→ Diagnostic: "User created with generic type: bool"User<SomeOtherClass> userWithSomeOtherClass = new();→ Diagnostic: "User created with generic type: SomeOtherClass"
Setup Notes
- Ensure your generator project references
Microsoft.CodeAnalysis.CSharpandMicrosoft.CodeAnalysis.AnalyzersNuGet packages. - If you need to handle additional contexts (like
User<T>being used as a method return type or property type), expand the syntax predicate to include those node types.
内容的提问来源于stack exchange,提问作者İsmail Furkan GÖKHASAN
相关产品推荐
相关产品推荐

