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<T>
找到“最贪婪”的构造函数并选择public Lazy(Func<T> valueFactory, bool isThreadSafe)
。由于我们没有告诉它如何处理布尔 isThreadSafe 参数,所以当它尝试解析 `Lazy' 时会抛出异常。
Lazy 的文档指出,默认 Lazy(Func<T> valueFactory)
构造函数的“线程安全模式”是 LazyThreadSafetyMode.ExecutionAndPublication
,这恰好是您通过将 true 传递给上述构造函数的 isThreadSafe 参数所得到的。因此,如果我们可以告诉 StructureMap 将true
传递给isThreadSafe
,我们将获得与调用我们实际上想要首先使用的构造函数相同的行为(例如Lazy(Func<T> valueFactory)
)。
简单地注册x.For(typeof(bool)).Use(y => true)
将是非常鲁莽和危险的,因为我们会告诉StructureMap 继续使用值true
作为任何 布尔值。相反,我们需要告诉 StructureMap 对于这个 one 布尔参数使用什么值,我们可以这样做。
x.For(typeof(Lazy<>)).Use(typeof(Lazy<>))
.CtorDependency<bool>("isThreadSafe").Is(true);
StructureMap 现在知道在解析 Lazy<T>
时使用 isThreadSafe 参数的“true”值。我们现在可以在构造函数参数中使用Lazy<T>
,并获得我认为您正在寻找的行为。
您可以阅读有关 Lazy 课程的更多详细信息here。
【讨论】:
注意:这与我对问题 6814961 的回答大部分 (90%) 相同,但实际上比那个问题更适合这个问题。 您是否有链接说明 StructureMap v3 默认情况下如何使用延迟加载?谢谢 我不会说“默认”。只是你不必再欺骗它了。在您的类的构造函数中,不要使用和 IFoo,而是使用 Lazy是的,确实如此。最新版本的 StructureMap (2.6.x) 是针对 .NET Framework 3.5 编译的,因此无法访问 .NET 4 中引入的 Lazy<T>
类型。但是,它确实支持相同的功能 - “不创建对象在使用之前一直注入”。您不依赖于Lazy<T>
,而是依赖于Func<T>
。不需要特殊的容器注册。
我已经包含了一个创建以下输出的示例程序:
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?的主要内容,如果未能解决你的问题,请参考以下文章
为啥默认 xcassets LaunchImage 不支持开箱即用的 iPhone5?