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

如何使用Microsoft DI容器传递构造函数参数,实现单文档单IUndoRedo实例

问题

需要配置Microsoft DI容器解析相关类型,期望能通过类似以下方式注册服务,且可轻松替换实现(比如将IParagraph的实现换成ParagraphVersion2):

var services = new ServiceCollection();
services.AddTransient<IHeader, Header>();
services.AddTransient<IParagraph, Paragraph>();
services.AddTransient<IFooter, Footer>();
services.AddTransient<IDocument, Document>();
services.AddTransient<IUndoRedo, UndoRedo>();

核心需求:每个Document实例对应唯一的IUndoRedo实例,该文档内的所有IObject对象必须使用这个IUndoRedo实例作为构造函数参数。目前仅通过属性注入实现,但容易创建不完整对象,寻求构造函数注入的DI配置方案。


解决方案

要实现文档与撤销重做实例的绑定,同时保留构造函数注入和实现替换的灵活性,推荐以下两种方案:

方案一:利用Scoped作用域自动关联实例

通过将IUndoRedo注册为Scoped服务,确保同一作用域内的Document和所有IObject共享同一个IUndoRedo实例,完全依赖DI容器管理生命周期。

1. 改造对象工厂

让工厂从DI容器中获取实例,而非手动创建,确保使用当前作用域的IUndoRedo:

public class ObjectFactory
{
    private readonly IServiceProvider _serviceProvider;

    public ObjectFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IObject Create(ObjectType objectType)
    {
        return objectType switch
        {
            ObjectType.Header => _serviceProvider.GetRequiredService<IHeader>(),
            ObjectType.Footer => _serviceProvider.GetRequiredService<IFooter>(),
            ObjectType.Paragraph => _serviceProvider.GetRequiredService<IParagraph>(),
            _ => throw new NotSupportedException(nameof(objectType))
        };
    }
}

2. 注册服务

var services = new ServiceCollection();
// 每个作用域对应一个唯一的IUndoRedo实例
services.AddScoped<IUndoRedo, UndoRedo>();
// 文档和所有对象实现注册为Scoped,确保同一作用域内共享UndoRedo
services.AddScoped<IHeader, Header>();
services.AddScoped<IParagraph, Paragraph>();
services.AddScoped<IFooter, Footer>();
services.AddScoped<IDocument, Document>();
// 注册工厂,用于动态创建对象
services.AddTransient<ObjectFactory>();

3. 使用方式

每次创建文档时,开启一个新的作用域,该作用域内的所有实例自动绑定同一个IUndoRedo:

using var provider = services.BuildServiceProvider();

// 创建第一个文档及关联对象
using (var scope = provider.CreateScope())
{
    var doc = scope.ServiceProvider.GetRequiredService<IDocument>();
    var factory = scope.ServiceProvider.GetRequiredService<ObjectFactory>();
    
    doc.Objects.Add(factory.Create(ObjectType.Header));
    doc.Objects.Add(factory.Create(ObjectType.Footer));
    doc.Objects.Add(factory.Create(ObjectType.Paragraph));
    doc.Objects.Add(factory.Create(ObjectType.Paragraph));
    
    // 验证:所有对象与文档共享同一个UndoRedo
    bool isShared = doc.Objects.All(o => o.UndoRedo == doc.UndoRedo);
    Console.WriteLine($"文档1的对象共享UndoRedo:{isShared}"); // 输出True
}

// 创建第二个文档及关联对象
using (var scope = provider.CreateScope())
{
    var doc = scope.ServiceProvider.GetRequiredService<IDocument>();
    var factory = scope.ServiceProvider.GetRequiredService<ObjectFactory>();
    
    doc.Objects.Add(factory.Create(ObjectType.Header));
    doc.Objects.Add(factory.Create(ObjectType.Footer));
    doc.Objects.Add(factory.Create(ObjectType.Paragraph));
    
    bool isShared = doc.Objects.All(o => o.UndoRedo == doc.UndoRedo);
    Console.WriteLine($"文档2的对象共享UndoRedo:{isShared}"); // 输出True
}

方案二:自定义文档工厂手动绑定实例

如果不想依赖DI作用域,可以通过自定义工厂,为每个文档创建专属的IUndoRedo,并在创建对象时传入该实例:

1. 定义文档工厂接口

public interface IDocumentFactory
{
    IDocument CreateDocument();
    IObject CreateObjectForDocument(IDocument document, ObjectType objectType);
}

2. 实现文档工厂

利用ActivatorUtilities从DI容器中创建对象,并传入文档的IUndoRedo实例:

public class DocumentFactory : IDocumentFactory
{
    private readonly IServiceProvider _serviceProvider;

    public DocumentFactory(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public IDocument CreateDocument()
    {
        // 创建专属的UndoRedo实例
        var undoRedo = _serviceProvider.GetRequiredService<IUndoRedo>();
        return new Document(undoRedo);
    }

    public IObject CreateObjectForDocument(IDocument document, ObjectType objectType)
    {
        var undoRedo = document.UndoRedo;
        // 从DI容器获取对象并传入指定的UndoRedo
        return objectType switch
        {
            ObjectType.Header => ActivatorUtilities.CreateInstance<Header>(_serviceProvider, undoRedo),
            ObjectType.Footer => ActivatorUtilities.CreateInstance<Footer>(_serviceProvider, undoRedo),
            ObjectType.Paragraph => ActivatorUtilities.CreateInstance<Paragraph>(_serviceProvider, undoRedo),
            _ => throw new NotSupportedException(nameof(objectType))
        };
    }
}

3. 注册服务

var services = new ServiceCollection();
// 每次获取都创建新的UndoRedo实例
services.AddTransient<IUndoRedo, UndoRedo>();
// 对象实现注册为Transient,方便替换
services.AddTransient<IHeader, Header>();
services.AddTransient<IParagraph, Paragraph>();
services.AddTransient<IFooter, Footer>();
// 注册自定义文档工厂
services.AddTransient<IDocumentFactory, DocumentFactory>();

4. 使用方式

using var provider = services.BuildServiceProvider();
var factory = provider.GetRequiredService<IDocumentFactory>();

// 创建第一个文档
var doc1 = factory.CreateDocument();
doc1.Objects.Add(factory.CreateObjectForDocument(doc1, ObjectType.Header));
doc1.Objects.Add(factory.CreateObjectForDocument(doc1, ObjectType.Footer));
doc1.Objects.Add(factory.CreateObjectForDocument(doc1, ObjectType.Paragraph));

// 创建第二个文档
var doc2 = factory.CreateDocument();
doc2.Objects.Add(factory.CreateObjectForDocument(doc2, ObjectType.Header));
doc2.Objects.Add(factory.CreateObjectForDocument(doc2, ObjectType.Footer));

待转换的原始代码(中文注释版)
public enum ObjectType
{
    Header,
    Footer,
    Paragraph
}

public interface IObject
{
    IUndoRedo UndoRedo { get; }
}

public interface IHeader : IObject { }
public class Header : IHeader
{
    public IUndoRedo UndoRedo { get; }

    public Header(IUndoRedo undoRedo)
    {
        UndoRedo = undoRedo;
    }
}

public interface IParagraph : IObject { }
public class Paragraph : IParagraph
{
    public IUndoRedo UndoRedo { get; }

    public Paragraph(IUndoRedo undoRedo)
    {
        UndoRedo = undoRedo;
    }
}

public interface IFooter : IObject { }
public class Footer : IFooter
{
    public IUndoRedo UndoRedo { get; }

    public Footer(IUndoRedo undoRedo)
    {
        UndoRedo = undoRedo;
    }
}

public interface IDocument
{
    IUndoRedo UndoRedo { get; }
    List<IObject> Objects { get; }
}

public interface IUndoRedo { }
public class UndoRedo : IUndoRedo { }

public class Document : IDocument
{
    public IUndoRedo UndoRedo { get; }
    public List<IObject> Objects { get; } = new();

    public Document(IUndoRedo undoRedo)
    {
        UndoRedo = undoRedo;
    }
}

public class ObjectFactory
{
    public IObject Create(ObjectType objectType, IUndoRedo undoRedo)
    {
        switch (objectType)
        {
            case ObjectType.Header:
                return new Header(undoRedo);
            case ObjectType.Footer:
                return new Footer(undoRedo);
            case ObjectType.Paragraph:
                return new Paragraph(undoRedo);
            default:
                throw new NotSupportedException(nameof(objectType));
        }
    }
}

internal class Program
{
    static void Main(string[] args)
    {
        var factory = new ObjectFactory();

        // 每个新文档对应唯一的UndoRedo实例,文档内所有对象共享该实例
        {
            var undoRedo1 = new UndoRedo();
            var document1 = new Document(undoRedo1);
            document1.Objects.Add(factory.Create(ObjectType.Header, undoRedo1));
            document1.Objects.Add(factory.Create(ObjectType.Footer, undoRedo1));
            document1.Objects.Add(factory.Create(ObjectType.Paragraph, undoRedo1));
            document1.Objects.Add(factory.Create(ObjectType.Paragraph, undoRedo1));
        }

        // 每个新文档对应唯一的UndoRedo实例,文档内所有对象共享该实例
        {
            var undoRedo2 = new UndoRedo();
            var document2 = new Document(undoRedo2);
            document2.Objects.Add(factory.Create(ObjectType.Header, undoRedo2));
            document2.Objects.Add(factory.Create(ObjectType.Footer, undoRedo2));
            document2.Objects.Add(factory.Create(ObjectType.Paragraph, undoRedo2));
        }
    }
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.17 02:27:03