Skip to content

高级管道操作

掌握复杂的管道模式和高级技巧

📋 文档信息

属性
难度高级
阅读时间40 分钟
前置知识管道操作、数据流转换、缓存机制
相关文档管道操作详解

🎯 学习目标

完成本文档后,你将能够:

  • ✅ 使用 WithComparer 自定义缓存行为
  • ✅ 使用 WithTrackingName 进行性能分析
  • ✅ 使用 ForAttributeWithMetadataName 高效过滤
  • ✅ 实现多源组合模式
  • ✅ 实现分组聚合模式
  • ✅ 实现条件管道模式

📚 快速导航

章节说明
WithComparer自定义比较器
WithTrackingName性能跟踪
ForAttributeWithMetadataName高效特性过滤
多源组合组合多个数据源
分组聚合按条件分组
条件管道条件分支

WithComparer

自定义比较器

使用自定义比较器来控制缓存行为:

csharp
using Microsoft.CodeAnalysis;
using System.Collections.Generic;

/// <summary>
/// 使用 WithComparer 自定义缓存行为
/// </summary>
public class WithComparerDemo
{
    /// <summary>
    /// 类信息
    /// </summary>
    public class ClassInfo
    {
        public string Name { get; set; }
        public string Namespace { get; set; }
        public List<string> Properties { get; set; }
    }
    
    /// <summary>
    /// 自定义比较器:只比较类名
    /// </summary>
    public class ClassInfoComparer : IEqualityComparer<ClassInfo>
    {
        public bool Equals(ClassInfo x, ClassInfo y)
        {
            if (x == null && y == null) return true;
            if (x == null || y == null) return false;
            
            // 只比较类名,忽略其他属性
            return x.Name == y.Name;
        }
        
        public int GetHashCode(ClassInfo obj)
        {
            return obj?.Name?.GetHashCode() ?? 0;
        }
    }
    
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        var classInfos = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: (node, _) => node is ClassDeclarationSyntax,
                transform: (ctx, _) =>
                {
                    var classDecl = (ClassDeclarationSyntax)ctx.Node;
                    var symbol = ctx.SemanticModel.GetDeclaredSymbol(classDecl);
                    
                    return new ClassInfo
                    {
                        Name = symbol.Name,
                        Namespace = symbol.ContainingNamespace.ToDisplayString(),
                        Properties = symbol.GetMembers()
                            .OfType<IPropertySymbol>()
                            .Select(p => p.Name)
                            .ToList()
                    };
                })
            .WithComparer(new ClassInfoComparer());  // 使用自定义比较器
        
        context.RegisterSourceOutput(classInfos, (spc, info) =>
        {
            var code = GenerateCode(info);
            spc.AddSource($"{info.Name}.g.cs", code);
        });
    }
    
    private string GenerateCode(ClassInfo info)
    {
        return $@"
// Generated for class: {info.Name}
namespace {info.Namespace}
{{
    partial class {info.Name}
    {{
        // Generated code
    }}
}}";
    }
}

WithTrackingName

性能跟踪

为管道步骤添加跟踪名称,便于性能分析和调试:

csharp
using Microsoft.CodeAnalysis;

/// <summary>
/// 使用 WithTrackingName 进行性能分析
/// </summary>
public class WithTrackingNameDemo
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 为每个管道步骤添加跟踪名称
        var classes = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: (node, _) => node is ClassDeclarationSyntax,
                transform: (ctx, _) => (ClassDeclarationSyntax)ctx.Node)
            .WithTrackingName("FindClasses");  // 跟踪名称
        
        var publicClasses = classes
            .Where((classDecl, _) =>
                classDecl.Modifiers.Any(m => m.IsKind(SyntaxKind.PublicKeyword)))
            .WithTrackingName("FilterPublicClasses");  // 跟踪名称
        
        var classNames = publicClasses
            .Select((classDecl, _) => classDecl.Identifier.Text)
            .WithTrackingName("ExtractClassNames");  // 跟踪名称
        
        context.RegisterSourceOutput(classNames, (spc, name) =>
        {
            var code = $"// Class: {name}";
            spc.AddSource($"{name}.g.cs", code);
        });
    }
}

使用场景:

  • 性能分析:识别慢的管道步骤
  • 调试:了解数据流经哪些步骤
  • 日志记录:记录管道执行情况

ForAttributeWithMetadataName

高效特性过滤

这是最高效的过滤方式,专门用于查找带有特定特性的符号:

csharp
using Microsoft.CodeAnalysis;
using System.Collections.Immutable;

/// <summary>
/// 使用 ForAttributeWithMetadataName 高效过滤
/// </summary>
[Generator]
public class ForAttributeDemo : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 查找所有带 [GenerateToString] 特性的类
        var classesWithAttribute = context.SyntaxProvider
            .ForAttributeWithMetadataName(
                fullyQualifiedMetadataName: "MyNamespace.GenerateToStringAttribute",
                predicate: (node, _) => node is ClassDeclarationSyntax,
                transform: (ctx, _) => GetClassInfo(ctx))
            .Where(info => info != null);
        
        context.RegisterSourceOutput(classesWithAttribute, (spc, info) =>
        {
            var code = GenerateToStringMethod(info);
            spc.AddSource($"{info.ClassName}.ToString.g.cs", code);
        });
    }
    
    /// <summary>
    /// 从语法上下文提取类信息
    /// </summary>
    private ClassInfo GetClassInfo(GeneratorAttributeSyntaxContext context)
    {
        // context.TargetSymbol 是带特性的符号
        var classSymbol = (INamedTypeSymbol)context.TargetSymbol;
        
        // context.TargetNode 是语法节点
        var classDecl = (ClassDeclarationSyntax)context.TargetNode;
        
        // context.Attributes 是特性列表
        var attribute = context.Attributes[0];
        
        // 提取特性参数
        var includeFields = false;
        if (attribute.ConstructorArguments.Length > 0)
        {
            includeFields = (bool)attribute.ConstructorArguments[0].Value;
        }
        
        // 获取所有属性
        var properties = classSymbol.GetMembers()
            .OfType<IPropertySymbol>()
            .Where(p => p.DeclaredAccessibility == Accessibility.Public)
            .Select(p => p.Name)
            .ToImmutableArray();
        
        // 获取所有字段(如果需要)
        var fields = includeFields
            ? classSymbol.GetMembers()
                .OfType<IFieldSymbol>()
                .Where(f => f.DeclaredAccessibility == Accessibility.Public)
                .Select(f => f.Name)
                .ToImmutableArray()
            : ImmutableArray<string>.Empty;
        
        return new ClassInfo(
            classSymbol.Name,
            classSymbol.ContainingNamespace.ToDisplayString(),
            properties,
            fields);
    }
    
    private string GenerateToStringMethod(ClassInfo info)
    {
        var members = info.Properties.Concat(info.Fields);
        var memberStrings = string.Join(", ", 
            members.Select(m => $"{m} = {{{m}}}"));
        
        return $@"
namespace {info.Namespace}
{{
    partial class {info.ClassName}
    {{
        public override string ToString()
        {{
            return $""{info.ClassName} {{ {memberStrings} }}"";
        }}
    }}
}}";
    }
    
    private record ClassInfo(
        string ClassName,
        string Namespace,
        ImmutableArray<string> Properties,
        ImmutableArray<string> Fields);
}

ForAttributeWithMetadataName 的优势:

  1. 性能最优:编译器级别的优化
  2. 自动缓存:只在特性改变时重新计算
  3. 类型安全:直接获取符号信息
  4. 简洁代码:一步完成过滤和转换

多源组合模式

组合多个数据源

组合编译选项、类声明和配置文件:

csharp
using Microsoft.CodeAnalysis;
using System.Collections.Immutable;

/// <summary>
/// 多源组合模式
/// </summary>
[Generator]
public class MultiSourceGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 数据源 1: 编译选项
        var compilationOptions = context.CompilationProvider
            .Select((comp, _) => new CompilationInfo(
                comp.AssemblyName,
                comp.Options.OutputKind));
        
        // 数据源 2: 类声明
        var classes = context.SyntaxProvider
            .ForAttributeWithMetadataName(
                "MyNamespace.GenerateAttribute",
                predicate: (node, _) => node is ClassDeclarationSyntax,
                transform: (ctx, _) => GetClassInfo(ctx));
        
        // 数据源 3: 配置文件
        var configFiles = context.AdditionalTextsProvider
            .Where(file => file.Path.EndsWith(".config.json"))
            .Select((file, ct) => ParseConfig(file, ct))
            .Collect();
        
        // 组合所有数据源
        var combined = classes
            .Combine(compilationOptions)
            .Combine(configFiles);
        
        // 使用组合的数据生成代码
        context.RegisterSourceOutput(combined, (spc, data) =>
        {
            var ((classInfo, compInfo), configs) = data;
            
            var code = GenerateCode(classInfo, compInfo, configs);
            spc.AddSource($"{classInfo.Name}.g.cs", code);
        });
    }
    
    private ClassInfo GetClassInfo(GeneratorAttributeSyntaxContext context)
    {
        var symbol = (INamedTypeSymbol)context.TargetSymbol;
        return new ClassInfo(
            symbol.Name,
            symbol.ContainingNamespace.ToDisplayString());
    }
    
    private ConfigData ParseConfig(AdditionalText file, CancellationToken ct)
    {
        var content = file.GetText(ct)?.ToString() ?? "";
        return new ConfigData(file.Path, content);
    }
    
    private string GenerateCode(
        ClassInfo classInfo,
        CompilationInfo compInfo,
        ImmutableArray<ConfigData> configs)
    {
        return $@"
// Generated for: {classInfo.Name}
// Assembly: {compInfo.AssemblyName}
// Output: {compInfo.OutputKind}
// Configs: {configs.Length}

namespace {classInfo.Namespace}
{{
    partial class {classInfo.Name}
    {{
        public static string AssemblyName => ""{compInfo.AssemblyName}"";
        public static int ConfigCount => {configs.Length};
    }}
}}";
    }
    
    private record ClassInfo(string Name, string Namespace);
    private record CompilationInfo(string AssemblyName, OutputKind OutputKind);
    private record ConfigData(string Path, string Content);
}

分组聚合模式

按命名空间分组

将数据分组并聚合处理:

csharp
using Microsoft.CodeAnalysis;
using System.Collections.Immutable;
using System.Linq;

/// <summary>
/// 分组聚合模式
/// </summary>
[Generator]
public class GroupingGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 获取所有类
        var classes = context.SyntaxProvider
            .ForAttributeWithMetadataName(
                "MyNamespace.GenerateAttribute",
                predicate: (node, _) => node is ClassDeclarationSyntax,
                transform: (ctx, _) =>
                {
                    var symbol = (INamedTypeSymbol)ctx.TargetSymbol;
                    return new ClassInfo(
                        symbol.Name,
                        symbol.ContainingNamespace.ToDisplayString());
                });
        
        // 收集所有类
        var allClasses = classes.Collect();
        
        // 按命名空间分组
        var groupedByNamespace = allClasses
            .Select((classes, _) =>
            {
                return classes
                    .GroupBy(c => c.Namespace)
                    .Select(g => new NamespaceGroup(
                        g.Key,
                        g.Select(c => c.Name).ToImmutableArray()))
                    .ToImmutableArray();
            });
        
        // 为每个命名空间生成代码
        context.RegisterSourceOutput(groupedByNamespace, (spc, groups) =>
        {
            foreach (var group in groups)
            {
                var code = GenerateNamespaceCode(group);
                var fileName = group.Namespace.Replace(".", "_");
                spc.AddSource($"{fileName}.Registry.g.cs", code);
            }
        });
    }
    
    private string GenerateNamespaceCode(NamespaceGroup group)
    {
        var classNames = string.Join(", ", 
            group.ClassNames.Select(n => $"\"{n}\""));
        
        return $@"
namespace {group.Namespace}
{{
    /// <summary>
    /// 自动生成的类注册表
    /// </summary>
    public static class GeneratedClassRegistry
    {{
        public static readonly string[] ClassNames = new[]
        {{
            {classNames}
        }};
        
        public static int Count => {group.ClassNames.Length};
    }}
}}";
    }
    
    private record ClassInfo(string Name, string Namespace);
    private record NamespaceGroup(
        string Namespace,
        ImmutableArray<string> ClassNames);
}

条件管道模式

根据条件选择不同路径

根据编译配置选择不同的代码生成策略:

csharp
using Microsoft.CodeAnalysis;

/// <summary>
/// 条件管道模式
/// </summary>
[Generator]
public class ConditionalPipelineGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // 获取编译配置
        var isDebugBuild = context.CompilationProvider
            .Select((comp, _) =>
            {
                // 检查是否是 Debug 构建
                return comp.Options.OptimizationLevel == OptimizationLevel.Debug;
            });
        
        // 获取类声明
        var classes = context.SyntaxProvider
            .ForAttributeWithMetadataName(
                "MyNamespace.GenerateAttribute",
                predicate: (node, _) => node is ClassDeclarationSyntax,
                transform: (ctx, _) => (INamedTypeSymbol)ctx.TargetSymbol);
        
        // 组合配置和类
        var combined = classes.Combine(isDebugBuild);
        
        // 根据配置选择不同的生成策略
        context.RegisterSourceOutput(combined, (spc, data) =>
        {
            var (classSymbol, isDebug) = data;
            
            string code;
            if (isDebug)
            {
                // Debug 模式:生成详细的调试代码
                code = GenerateDebugCode(classSymbol);
            }
            else
            {
                // Release 模式:生成优化的代码
                code = GenerateOptimizedCode(classSymbol);
            }
            
            spc.AddSource($"{classSymbol.Name}.g.cs", code);
        });
    }
    
    private string GenerateDebugCode(INamedTypeSymbol symbol)
    {
        return $@"
namespace {symbol.ContainingNamespace.ToDisplayString()}
{{
    partial class {symbol.Name}
    {{
        // Debug 模式:包含详细日志
        private static void Log(string message)
        {{
            System.Diagnostics.Debug.WriteLine(
                $""[{symbol.Name}] {{message}}"");
        }}
        
        partial void OnCreated()
        {{
            Log(""Instance created"");
        }}
    }}
}}";
    }
    
    private string GenerateOptimizedCode(INamedTypeSymbol symbol)
    {
        return $@"
namespace {symbol.ContainingNamespace.ToDisplayString()}
{{
    partial class {symbol.Name}
    {{
        // Release 模式:无日志,优化性能
        [System.Runtime.CompilerServices.MethodImpl(
            System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)]
        partial void OnCreated() {{ }}
    }}
}}";
    }
}

🔑 关键要点

高级模式总结

  1. WithComparer: 自定义缓存行为
  2. WithTrackingName: 性能分析和调试
  3. ForAttributeWithMetadataName: 最高效的特性过滤
  4. 多源组合: 组合多个数据源
  5. 分组聚合: 按条件分组处理
  6. 条件管道: 根据条件选择不同路径

使用场景

模式使用场景
WithComparer需要自定义相等性比较
WithTrackingName性能分析和调试
ForAttributeWithMetadataName查找带特性的类型
多源组合需要多个数据源
分组聚合需要按条件分组
条件管道需要条件分支

🔗 相关资源


🚀 下一步


最后更新: 2026-02-05

基于 MIT 许可发布