Roo*_*ian 13 c# delegates dependency-injection inversion-of-control func
例:
public class BusinessTransactionFactory<T> where T : IBusinessTransaction
{
readonly Func<Type, IBusinessTransaction> _createTransaction;
public BusinessTransactionFactory(Func<Type, IBusinessTransaction> createTransaction)
{
_createTransaction = createTransaction;
}
public T Create()
{
return (T)_createTransaction(typeof(T));
}
}
Run Code Online (Sandbox Code Playgroud)
使用相同的容器设置代码:
public class DependencyRegistration : Registry
{
public DependencyRegistration()
{
Scan(x =>
{
x.AssembliesFromApplicationBaseDirectory();
x.WithDefaultConventions();
x.AddAllTypesOf(typeof(Repository<>));
x.ConnectImplementationsToTypesClosing(typeof(IRepository<>));
});
Scan(x =>
{
x.AssembliesFromApplicationBaseDirectory();
x.AddAllTypesOf<IBusinessTransaction>();
For(typeof(BusinessTransactionFactory<>)).Use(typeof(BusinessTransactionFactory<>));
For<Func<Type, IBusinessTransaction>>().Use(type => (IBusinessTransaction)ObjectFactory.GetInstance(type));
});
For<ObjectContext>().Use(() => new ManagementEntities());
}
}
Run Code Online (Sandbox Code Playgroud)
你怎么看?
Mar*_*ann 25
机械学
在机械层面上,使用委托是完全没问题的,因为委托基本上是一个匿名的角色接口.换句话说,无论是注入委托还是接口或抽象基类都无关紧要.
概念
在概念层面,重要的是要记住依赖注入的目的.您可能因为与我不同的原因使用DI,但IMO的目的是提高代码库的可维护性.
是否通过注入代理而不是接口来实现这一目标是值得怀疑的.
委托作为依赖项
第一个问题是代表沟通意图的程度.有时,接口名称本身会传达意图,而标准委托类型几乎不会.
例如,单独的类型在这里没有太多意图:
public BusinessTransactionFactory(Func<Type, IBusinessTransaction> createTranscation)
Run Code Online (Sandbox Code Playgroud)
幸运的是,该createTranscation
名称仍然暗示了委托所扮演的角色,但只是考虑(为了论据的缘故)如果作者不那么小心,那么构造函数的可读性如何:
public BusinessTransactionFactory(Func<Type, IBusinessTransaction> func)
Run Code Online (Sandbox Code Playgroud)
换句话说,使用委托将焦点从类型名称转移到参数名称.这不一定是个问题 - 我只是指出你需要意识到这种转变.
可发现性与可组合性
另一个问题是关于实现依赖关系的类型的可发现性与可组合性.举个例子,这两个实现公共Func<Type, IBusinessTransaction>
:
t => new MyBusinessTransaction()
Run Code Online (Sandbox Code Playgroud)
和
public class MyBusinessTransactionFactory
{
public IBusinessTransaction Create(Type t)
{
return new MyBusinessTransaction();
}
}
Run Code Online (Sandbox Code Playgroud)
但是,在类的情况下,具体的非虚Create
方法与所需的委托匹配几乎是偶然的.它非常可组合,但不是很容易被发现.
另一方面,当我们使用接口时,类在实现接口时成为is-a关系的一部分,因此通常更容易找到所有实现者和组并相应地组合它们.
请注意,这不仅适用于程序员阅读代码,也适用于DI容器.因此,在使用接口时,更容易实现Convention over Configuration.
与RAP相比,1:1接口
有些人注意到,当尝试使用DI时,他们最终会得到很多1:1接口(例如IFoo
,相应的Foo
类).在这些情况下,接口(IFoo
)似乎是多余的,似乎很容易摆脱接口并使用委托.
但是,许多1:1接口实际上是违反重用抽象原则的症状.当您重构代码库以在多个位置重用相同的抽象时,将抽象的角色显式建模为接口(或抽象基类)是有意义的.
结论
接口不仅仅是机制.它们在应用程序代码库中明确地模拟角色.中心角色应该由接口表示,而一次性工厂及其同类可以作为代理使用和实现.
我个人不喜欢Func<T>
在我的类中看到参数作为依赖项,因为我认为它使代码的可读性降低,但我知道许多开发人员不同意我的观点.某些容器(如Autofac)甚至允许您解析Func<T>
委托(通过返回a () => container.Resolve<T>()
).
但是,有两种情况我不介意注入Func<T>
代表:
Func<T>
构造函数参数的类位于组合根中时,因为在这种情况下它是容器配置的一部分,我不考虑应用程序设计的那一部分.Func<T>
在应用程序中以单一类型注入时.当更多服务依赖于相同的服务时Func<T>
,创建一个特殊的I[SomeType]Factory
接口并注入它会更好.