Func <in t,=“”out =“”tresult =“”>在应用依赖注入时是否适合用作ctor arg?</in>

时间:2011-11-27 13:25:36

标签: c# dependency-injection delegates 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));
    }
}

利用相同的容器设置代码:

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());
    }
}

您怎么看?

7 个答案:

答案 0 :(得分:25)

<强>力学

在机械层面上,使用代表是完全没问题的,因为代表基本上是anonymous Role Interface。换句话说,无论是注入委托还是接口或抽象基类都没关系。

<强>概念

在概念层面,重要的是要记住依赖注入的目的。您可能因为与我不同的原因使用DI,但IMO的目的是提高代码库的可维护性

这个目标是否通过注入代理而不是接口来实现是值得怀疑的。

委托作为依赖项

第一个问题是代表传达意图的程度。有时,接口名称本身就会传达意图,而标准委托类型则很难。

例如, type 本身并没有太多意图:

public BusinessTransactionFactory(Func<Type, IBusinessTransaction> createTranscation)

幸运的是,createTranscation名称仍然暗示了委托人所扮演的角色,但只是考虑(为了论据的缘故)如果作者不那么小心,构造函数的可读性如何:

public BusinessTransactionFactory(Func<Type, IBusinessTransaction> func)

换句话说,使用委托将焦点从类型名称转移到参数名称。这不一定是个问题 - 我只是指出你需要意识到这种转变。

可发现性与可组合性

另一个问题是关于实现依赖关系的类型的可发现性与可组合性。作为示例,这两个都实现了公共Func<Type, IBusinessTransaction>

t => new MyBusinessTransaction()

public class MyBusinessTransactionFactory
{
    public IBusinessTransaction Create(Type t)
    {
        return new MyBusinessTransaction();
    }
}

但是,在类的情况下,具体的非虚拟Create方法与所需的委托匹配几乎是偶然的。 它非常易于组合,但不是很容易被发现

另一方面,当我们使用接口时,类在实现接口时会成为 is-a 关系的一部分,因此通常会更容易找到所有实现者和组并相应地组合它们。 / p>

请注意,这不仅适用于程序员阅读代码,也适用于DI容器。因此,当您使用接口时,更容易实现约定优于配置

1:1接口与RAP

有些人注意到,在尝试使用DI时,他们最终会得到很多1:1接口(例如IFoo和相应的Foo类)。在这些情况下,接口(IFoo)似乎是多余的,似乎很容易摆脱接口并改为使用委托。

然而,many 1:1 interfaces are really a symptom of a violation of the Reused Abstractions Principle。当您重构代码库以在多个位置重用相同的抽象时,将该抽象的角色显式建模为接口(或抽象基类)是有意义的。

<强>结论

接口不仅仅是机制。它们在应用程序代码库中明确地模拟角色。中心角色应该由接口表示,而一次性工厂及其同类可以作为代表使用和实现。

答案 1 :(得分:5)

我个人不喜欢在我的类中看到Func<T>参数作为依赖项,因为我认为它使代码的可读性降低,但我知道许多开发人员不同意我的看法。某些容器(例如Autofac)甚至允许您解析Func<T>个委托(通过返回() => container.Resolve<T>())。

但是有两种情况我不介意注入Func<T>代表:

  1. 当获取Func<T>构造函数参数的类位于Composition Root时,因为在这种情况下它是容器配置的一部分,我不认为应用程序的那部分设计
  2. 在应用程序中以单一类型注入Func<T>时。当更多服务开始依赖于相同的Func<T>时,可读性最好是创建一个特殊的I[SomeType]Factory接口并注入该接口。

答案 2 :(得分:4)

过去,我使用了你提到的模式(委托)和单个方法接口。目前,我倾向于使用单一方法接口。

使用界面时,模拟依赖项所需的代码更容易阅读,并且您还可以利用自动键入的工厂,如@devdigital所述。

另一件需要考虑的事情是,在使用接口时,您不会有代理调用的开销,这在高性能应用程序中可能是一个有效的考虑因素。

答案 3 :(得分:3)

@史蒂文的回答经过深思熟虑;就个人而言,我发现Func<T>参数的使用有限。

我不明白的是BusinessTransactionFactory<T>IBusinessTransactionFactory<T>的价值。这些只是委托人的包装。据推测,你正在其他地方注射IBusinessTransactionFactory<T>。为什么不在那里注入代表?

我认为Func<T>(和Func<T, TResult>等)是工厂。对我来说,你有一个工厂类实现一个包装工厂委托的工厂接口,这似乎是多余的。

答案 4 :(得分:2)

我认为这里真正的问题是依赖类型是否应该被视为合同。

对于Func委托,您声明对某个方法结构的依赖,但仅此而已。您不能通过查看此结构来推断接受的输入值的范围(前置条件),对预期输出的约束(后置条件)或它们的确切含义。例如,null是否为可接受的返回值?如果是,那该怎么解释?

对于接口,您有双方同意的合同:消费者明确声明它需要特定的接口,并且实现者明确声明提供它。这超出了结构:接口的文档将讨论前置条件,后置条件和语义。

答案 5 :(得分:1)

是的,这是完全可以接受的,尽管如果你使用的是容器,现在越来越多的容器支持自动打字的工厂,这是更好的选择。

答案 6 :(得分:0)

如果你注入Func我瘦了代码将是懒惰的,所以在某些情况下,如果你的类依赖于许多其他依赖项,那么在这种情况下注入Funct可能更好。我是对的。

抱歉我的英文不好