C# 建造商DI的自动工厂发电机

C# 建造商DI的自动工厂发电机,c#,dependency-injection,factory,factory-pattern,reflection.emit,C#,Dependency Injection,Factory,Factory Pattern,Reflection.emit,我希望在不传递任何内核容器的情况下使用基于工厂的依赖项注入,这样就不可能在不从顶部显式传递依赖项的情况下实例化类 手动执行此操作需要在引导中使用如下代码: static void Main(string[] args) { // simplified example, can require classes in reality ABFactory abFactory = (data1) => new AB(data1); A

我希望在不传递任何内核容器的情况下使用基于工厂的依赖项注入,这样就不可能在不从顶部显式传递依赖项的情况下实例化类

手动执行此操作需要在引导中使用如下代码:

    static void Main(string[] args)
    {
        // simplified example, can require classes in reality
        ABFactory abFactory = (data1) => new AB(data1);
        ACAFactory acaFactory = (data1) => new ACA(data1);
        ACFactory acFactory = (x) => new AC(x, acaFactory);
        IA a = new A(1, new AA(1, new AAA(), new AAB()), abFactory, acFactory);
        a.Action(123);
    }
当工厂被定义为

delegate IAB ABFactory(string data1);
delegate IAC ACFactory(int x);
delegate IACA ACAFactory(int data1);
有什么我可以用来使工厂建设更容易甚至自动化的吗?使用不同的工厂类型支持池、线程本地缓存等

更新

一些真实的代码示例:

public interface IItemSetSpawnController
{
    void TransitSpawned();
}

public class ItemSetSpawnController : IItemSetSpawnController
{
    readonly GameMap.ItemSet _set;
    readonly LootableFactoryDelegate _lootableFactory;
    readonly IFiber _fiber;

    readonly int _defaultRespawnTime;

    public ItemSetSpawnController([NotNull] GameMap.ItemSet set, int defaultRespawnTime, [NotNull] LootableFactoryDelegate lootableFactory, IFiber fiber)
    {
        if (set == null) throw new ArgumentNullException(nameof(set));
        if (lootableFactory == null) throw new ArgumentNullException(nameof(lootableFactory));
        if (set.Items.Count == 0) throw new ArgumentException("Empty set", nameof(set));
        _set = set;
        _lootableFactory = lootableFactory;
        _fiber = fiber;
        _defaultRespawnTime = defaultRespawnTime;
    }

    public void TransitSpawned()
    {
        // Fiber.Schedule for respawning
    }
}

public delegate IItemSetSpawnController ItemSetSpawnControllerFactory(
    [NotNull] GameMap.ItemSet set, int defaultRespawnTime, [NotNull] LootableFactoryDelegate lootableFactory, IFiber fiber);


protected virtual void AddMapLootables()
{
    foreach (var set in ItemSets)
    {
        if (set.Items.Count == 0) continue;
        var c = ItemSetSpawnControllerFactory(
            set,
            Settings.LootRespawnTime,
            LootableFactory,
            ExecutionFiber);
        c.TransitSpawned();
    }

}

在我看来,您的组件是用运行时数据初始化的。这是因为这将使您的DI配置变得非常复杂,我认为您的情况就是这样

如果组件的构造函数同时依赖于服务依赖项和运行时数据,则将运行时数据移出构造函数并在运行时提供给组件,而不是使用工厂来初始化组件。您可以通过组件的方法传递运行时值来实现这一点,也可以向组件中注入一个服务,该服务允许组件在初始化后在运行时请求该值

选择哪个选项取决于运行时值的使用是否是实现细节。如果它是抽象的基本部分,那么该值应该是抽象的方法签名的一部分。如果它与抽象无关,并且只有组件本身应该知道它,那么注入允许访问此运行时值的服务是最明显的解决方案

更新

如果您将运行时数据移出构造函数,您将能够消除将工厂用作额外抽象层的需要,并且您可以将代码更改为如下内容:

public interface IItemSetSpawnController {
    void TransitSpawned(GameMap.ItemSet set);
}

public class ItemSetSpawnController : IItemSetSpawnController {
    readonly LootableFactoryDelegate _lootableFactory;
    readonly IFiber _fiber;
    readonly ISessings settings;
    public ItemSetSpawnController(ISessings settings,
        [NotNull] LootableFactoryDelegate lootableFactory, IFiber fiber) {
        _lootableFactory = lootableFactory;
        _fiber = fiber;
        _settings = settings;
    }
    public void TransitSpawned([NotNull] GameMap.ItemSet set) {
        if (set == null) throw new ArgumentNullException(nameof(set));
        // If LootRespawnTime is a config value that doesn't change after startup, you
        // can more it back into the constructor of the controller.
        int defaultRespawnTime = _settings.LootRespawnTime;
        // Fiber.Schedule for respawning
    }
}

class MapLooter {
    IItemSetSpawnController controller;
    public MapLooter(IItemSetSpawnController controller){
        this.controller = controller;
    }
    public void AddMapLootables() {
        foreach (var set in ItemSets) {
            if (set.Items.Count == 0) continue;
            this.controller.TransitSpawned(set);
        }
    }
}

Autofac内置了这个概念:是的,这是一种常用的方法-将上下文数据传递给构造函数。实际上,如果一个类在没有上下文的情况下被实例化,那么它应该是单例的。我知道你建议将这样的上下文初始化代码移动到包含在接口中的一个单独的方法中,但这对我来说有点奇怪…@Vlad:如果你更新你的问题,并用你试图创建的抽象和组件的实际名称使代码更具体,我可以举一个具体的例子来说明如何改进您的设计。如果我的代码不清楚,我很抱歉,但是。。ItemSetPawnController的目的是包含带计时器的重新启动逻辑,订阅单个集合的拾取事件。因此,应该为每个项目集构造它。@Vlad:应该重新创建它,还是希望重新创建它?根据我的经验,让应用程序组件保持无状态要好得多。这使得代码更简单,对象图更容易验证。它不是一次性对象。正如我所说,它在整个游戏生命周期中为单个项目集维护一个重生逻辑。游戏也是为服务器上的每个游戏室实例化的运行时对象。在我的实践中,处理附加到上下文类比使用一个实例来管理对象集合更好。例如,它允许对某些项目集进行非标准的重新命名实现。我的应用程序中几乎所有的东西都是上下文连接的运行时对象,甚至每个游戏实例的设置都可能不同,只有少数单个实例例外。