Structuremap是否支持Lazy开箱即用?

Rya*_*son 10 .net c# structuremap dependencies dependency-injection

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

Mel*_*Mel 13

更新: 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>));
});
Run Code Online (Sandbox Code Playgroud)

这很好用,但你必须单独注册每个类型.如果你能利用更基于惯例的方法,那就更好了.理想情况下,以下语法会很好.

x.For(typeof(Lazy<>)).Use(typeof(Lazy<>));
Run Code Online (Sandbox Code Playgroud)

这种语法实际上有点...... 不幸的是,在运行时,StructureMap将尝试找到"最贪婪"的构造函数Lazy<T>并以此为基础public Lazy(Func<T> valueFactory, bool isThreadSafe).由于我们没有告诉它如何处理boolean isThreadSafe参数,因此当它尝试解析"Lazy"时会抛出异常.

Lazy的文档说明了默认Lazy(Func<T> valueFactory)构造函数的"线程安全模式" LazyThreadSafetyMode.ExecutionAndPublication,这恰好是通过将true传递给上面构造函数的isThreadSafe参数得到的.所以,如果我们能告诉StructureMap通过trueisThreadSafe,我们会得到相同的行为,如果我们所谓的我们其实是想在第一时间(例如,使用构造函数Lazy(Func<T> valueFactory)).

简单地注册x.For(typeof(bool)).Use(y => true)将是非常鲁莽和危险的,因为我们将告诉StructureMap继续并在任何地方使用任何布尔值true的值.相反,我们需要告诉StructureMap使用什么值仅这一个布尔参数,我们可以这样做.

x.For(typeof(Lazy<>)).Use(typeof(Lazy<>))
 .CtorDependency<bool>("isThreadSafe").Is(true);
Run Code Online (Sandbox Code Playgroud)

StructureMap现在知道在解析时对isThreadSafe参数使用"true"的值Lazy<T>.我们现在可以Lazy<T>在构造函数参数中使用,并获得我认为您正在寻找的行为.

您可以在此处详细了解Lazy类.


Jos*_*gan 5

是的,它确实.最新版本的StructureMap(2.6.x)是针对.NET Framework 3.5编译的,因此无法访问Lazy<T>.NET 4中引入的类型.但是,它确实支持相同的功能 - "不创建注入的对象直到它被使用".你不依赖于a Lazy<T>,而是依靠a Func<T>.无需特殊的集装箱登记.

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

Created Consumer
Consuming
Created Helper
Helping
Run Code Online (Sandbox Code Playgroud)

Sample.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");
    }
}
Run Code Online (Sandbox Code Playgroud)