Structuremap 是不是支持开箱即用的 Lazy?

Posted

技术标签:

【中文标题】Structuremap 是不是支持开箱即用的 Lazy?【英文标题】:Does Structuremap support Lazy out of the box?Structuremap 是否支持开箱即用的 Lazy? 【发布时间】:2011-10-12 07:43:03 【问题描述】:

structuremap 是否允许您以惰性方式进行构造函数注入? 意思是在使用之前不创建注入的对象?

【问题讨论】:

为什么一个对象会有一个没有被使用的依赖? 好问题,有效点。如果不是所有的类方法都使用注入的对象。有些类可以注入多个对象,但并非每个方法都使用每个对象。也许那是我想的气味? 我认为一个不使用依赖的类很可能违反了单一职责原则。我不知道你的班级在做什么,所以我不能判断,但它可能是它试图做两件事的情况,可能是时候将它们分成不同的班级了。话虽如此,如果它是一次性的,它可能不会受到伤害,对象的创建成本很低,所以我怀疑目前性能是一个很大的问题。 我不得不在至少一个方面不同意 Phill 关于违反 SRP 的观点。逻辑已分解为多个类,这就是它们被注入的原因。想象一下,您有多个依赖项,并非所有依赖项都立即需要,其中一些可能根本不会被调用。构建这些依赖项将是一种浪费,因此将其推迟到实际调用通过对我来说似乎是一个完全有效的决定。 我认为你的声明是有效的梅尔。我不希望我的调用程序集必须知道太多,或者进行一系列“智能”调用,比如从 UI 层到 biz 层。商业层中封装的一些决策更多地根据最终用户的行为进行分组,并且成功执行该行为所需的规则和步骤可能具有 UI 不必担心的几个依赖项。 【参考方案1】:

更新: StructureMap v3 开箱即用地实现了这一点,因此不再需要这个技巧。


StructureMap 版本 2 没有,但是通过一些技巧,您可以让它完成我相信您正在寻找的事情。首先,您已经可以像这样手动连接Lazy<T> 实例:

container = new Container(x =>

    x.Scan(y =>
    
        y.TheCallingAssembly();
        y.WithDefaultConventions();
    );

    x.For<Lazy<IFoo>>().Use(y => new Lazy<IFoo>(y.GetInstance<Foo>));
    x.For<Lazy<IBar>>().Use(y => new Lazy<IBar>(y.GetInstance<Bar>));
    x.For<Lazy<IBaz>>().Use(y => new Lazy<IBaz>(y.GetInstance<Baz>));
);

这很好用,但是您必须单独注册每种类型。如果您可以利用更基于约定的方法,那就更好了。理想情况下,以下语法会很好。

x.For(typeof(Lazy<>)).Use(typeof(Lazy<>));

这种语法确实有效……有点。不幸的是,在运行时,StructureMap 将尝试为Lazy&lt;T&gt; 找到“最贪婪”的构造函数并选择public Lazy(Func&lt;T&gt; valueFactory, bool isThreadSafe)。由于我们没有告诉它如何处理布尔 isThreadSafe 参数,所以当它尝试解析 `Lazy' 时会抛出异常。

Lazy 的文档指出,默认 Lazy(Func&lt;T&gt; valueFactory) 构造函数的“线程安全模式”是 LazyThreadSafetyMode.ExecutionAndPublication,这恰好是您通过将 true 传递给上述构造函数的 isThreadSafe 参数所得到的。因此,如果我们可以告诉 StructureMap 将true 传递给isThreadSafe,我们将获得与调用我们实际上想要首先使用的构造函数相同的行为(例如Lazy(Func&lt;T&gt; valueFactory))。

简单地注册x.For(typeof(bool)).Use(y =&gt; true) 将是非常鲁莽和危险的,因为我们会告诉StructureMap 继续使用值true 作为任何 布尔值。相反,我们需要告诉 StructureMap 对于这个 one 布尔参数使用什么值,我们可以这样做。

x.For(typeof(Lazy<>)).Use(typeof(Lazy<>))
 .CtorDependency<bool>("isThreadSafe").Is(true);

StructureMap 现在知道在解析 Lazy&lt;T&gt; 时使用 isThreadSafe 参数的“true”值。我们现在可以在构造函数参数中使用Lazy&lt;T&gt;,并获得我认为您正在寻找的行为。

您可以阅读有关 Lazy 课程的更多详细信息here。

【讨论】:

注意:这与我对问题 6814961 的回答大部分 (90%) 相同,但实际上比那个问题更适合这个问题。 您是否有链接说明 StructureMap v3 默认情况下如何使用延迟加载?谢谢 我不会说“默认”。只是你不必再欺骗它了。在您的类的构造函数中,不要使用和 IFoo,而是使用 Lazy。在您使用 .Value 检索它之前,StructureMap 不会真正构建 Foo。我通常从构造函数中获取惰性依赖项,并将它们存储在支持变量中,然后通过诸如“private IFoo Foo get return _foo.Value; ”之类的属性公开它们 你能说明 Lazy 现在是如何开箱即用的吗? 我的意思是你不必再告诉 StructureMap 如何处理惰性依赖项了。在您的服务/控制器/任何东西的构造函数中,只需将依赖项键入为 Lazy 而不是简单的 IThingService。 StructureMap 应该推迟创建实例,直到您实际使用它。【参考方案2】:

是的,确实如此。最新版本的 StructureMap (2.6.x) 是针对 .NET Framework 3.5 编译的,因此无法访问 .NET 4 中引入的 Lazy&lt;T&gt; 类型。但是,它确实支持相同的功能 - “不创建对象在使用之前一直注入”。您不依赖于Lazy&lt;T&gt;,而是依赖于Func&lt;T&gt;。不需要特殊的容器注册。

我已经包含了一个创建以下输出的示例程序:

Created Consumer
Consuming
Created Helper
Helping

示例.cs:

class Program

    static void Main(string[] args)
    
        var container = new Container(x =>
        
            x.For<IConsumer>().Use<Consumer>();
            x.For<IHelper>().Use<Helper>();
        );

        var consumer = container.GetInstance<IConsumer>();
        consumer.Consume();
    


public class Consumer : IConsumer

    private readonly Func<IHelper> _helper;

    public Consumer(Func<IHelper> helper)
    
        _helper = helper;
        Console.WriteLine("Created Consumer");
    

    public void Consume()
    
        Console.WriteLine("Consuming");
        _helper().Help();
    


public interface IConsumer

    void Consume();


public interface IHelper

    void Help();


public class Helper : IHelper

    public Helper()
    
        Console.WriteLine("Created Helper");
    

    public void Help()
    
        Console.WriteLine("Helping");
    

【讨论】:

我会说 StructureMap 支持惰性(小“l”),但不支持惰性(大“L”)。也许我对问题中的大写阅读过多,但我认为它很重要。我还使用了 Func 技巧。它实现了相同的结果,但需要您在每次使用时添加括号,这应该表明 Func 将在每次使用时执行。或者,您可以在 Func 的支持字段前面添加一个附加属性,并执行类似“return _helper ?? (_helper = _helperFunc()); 问题的意图似乎是第二句话。当对象被实例化时,用户想要延迟。我就是这么回答的。

以上是关于Structuremap 是不是支持开箱即用的 Lazy?的主要内容,如果未能解决你的问题,请参考以下文章

任何 .NET ORM 是不是支持开箱即用的本地化实体?

为啥默认 xcassets LaunchImage 不支持开箱即用的 iPhone5?

开箱即用的 Prometheus 告警规则集

Magicodes.Pay,打造开箱即用的统一支付库,已提供ABP模块封装

开箱即用的 Prometheus 告警规则集

Groovy:没有开箱即用的 stringToMap 吗?