You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

能否借助Roslyn实现编译时代码生成以兼容遗留DI场景?

Absolutely, this is totally achievable with Roslyn-powered compile-time code generation, and the CodeGeneration.Roslyn.BuildTime package is exactly the tool you need here. Your core idea—creating a wrapper class for IFoo<T> that delegates to a transient ISuperFoo<T> resolved via DI— is spot-on, and Roslyn can handle the compile-time code generation seamlessly, just like it does for async/await state machines.

Core Concept Recap

Your plan makes perfect sense:

  • Leave the legacy library untouched (since you can't modify it)
  • Use ISuperFoo<T> to mark types that need transient lifecycle
  • Generate compile-time wrappers that implement IFoo<T> (so the legacy library picks them up)
  • Each wrapper resolves a transient ISuperFoo<T> instance from your DI container when Execute is called
Step-by-Step Implementation

1. Define Your Interfaces & DI Container

First, make sure your base interfaces and container are set up correctly:

// Your existing legacy interface
public interface IFoo<T> 
{ 
    void Execute(T obj); 
}

// New interface for transient implementations
public interface ISuperFoo<T> : IFoo<T> { } // Inheritance is optional, but simplifies alignment

// Your singleton DI container (adjust based on your actual container, e.g., Autofac, Microsoft.Extensions.DependencyInjection)
public class Container
{
    public static Container Instance { get; } = new Container();
    public T GetInstance<T>() 
    {
        // Replace with your actual DI resolution logic
        return (T)Activator.CreateInstance(typeof(T));
    }
}

2. Set Up the Roslyn Code Generator

First, install the CodeGeneration.Roslyn.BuildTime NuGet package in your project.

Then create an incremental generator to auto-produce the FooWrapper<T> classes:

using CodeGeneration.Roslyn;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

[Generator]
public class FooWrapperGenerator : IIncrementalGenerator
{
    public void Initialize(IncrementalGeneratorInitializationContext context)
    {
        // Find all non-abstract classes that implement ISuperFoo<T>
        var superFooImplementations = context.SyntaxProvider
            .CreateSyntaxProvider(
                predicate: (node, _) => node is ClassDeclarationSyntax classDecl && 
                                       classDecl.BaseList?.Types.Any(t => 
                                           t.Type.ToString().StartsWith("ISuperFoo<")) == true,
                transform: (ctx, _) => ctx.SemanticModel.GetDeclaredSymbol(ctx.Node) as INamedTypeSymbol)
            .Where(symbol => symbol != null && !symbol.IsAbstract && !symbol.IsInterface);

        // Generate a FooWrapper<T> for each matching ISuperFoo<T> implementation
        context.RegisterSourceOutput(superFooImplementations, (spc, symbol) =>
        {
            var typeParam = symbol.TypeArguments[0];
            var wrapperName = $"FooWrapper<{typeParam.Name}>";
            var namespaceName = symbol.ContainingNamespace.ToDisplayString();

            var sourceCode = $@"
using System.ComponentModel;
using {namespaceName};

[EditorBrowsable(EditorBrowsableState.Never)]
public class {wrapperName} : IFoo<{typeParam.Name}>
{{
    private readonly Container _container;

    public {wrapperName}(Container container)
    {{
        _container = container;
    }}

    public void Execute({typeParam.Name} obj)
    {{
        _container.GetInstance<ISuperFoo<{typeParam.Name}>>().Execute(obj);
    }}
}}";

            spc.AddSource($"{wrapperName}.generated.cs", sourceCode);
        });
    }
}

3. Configure DI for Transient Lifecycle

Register your ISuperFoo<T> implementations as transient in your DI container. For example, with Microsoft.Extensions.DependencyInjection:

var services = new ServiceCollection();
// Register each ISuperFoo<T> as transient
services.AddTransient<ISuperFoo<int>, MyIntSuperFoo>();
services.AddTransient<ISuperFoo<string>, MyStringSuperFoo>();
// Register your singleton container (if needed for wrapper injection)
services.AddSingleton(Container.Instance);
var serviceProvider = services.BuildServiceProvider();

// Update your Container's GetInstance to use the service provider
Container.Instance.GetInstance = (type) => serviceProvider.GetService(type);

4. Let the Legacy Library Do Its Thing

The legacy library scans for all IFoo<T> implementations—including the auto-generated FooWrapper<T> classes. When it instantiates these wrappers as singletons, each Execute call will resolve a fresh transient ISuperFoo<T> instance from your DI container, exactly what you need.

Key Tips for Success
  • Namespace Alignment: Ensure the generated wrappers are in the same namespace as IFoo<T> so the legacy library can find them.
  • Hide Wrappers: The [EditorBrowsable(EditorBrowsableState.Never)] attribute keeps your IDE clean by hiding the auto-generated wrappers from intellisense.
  • Container Access: If your legacy library doesn't support constructor injection for IFoo<T> implementations, you can fall back to using the static Container.Instance directly in the wrapper instead of constructor injection.
  • Performance: Using IIncrementalGenerator ensures the generator only runs when relevant types change, keeping compile times fast.

内容的提问来源于stack exchange,提问作者vtscop

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:33:12