简体   繁体   English

使用DI的单一方法委托与接口

[英]Delegate vs interface with a single method for DI

I have a task doing some background work which I want to throttle. 我有一个任务在做一些我想节制的后台工作。 I want to inject the void Throttle(taskState) method. 我想注入void Throttle(taskState)方法。 It can be as simple as Thread.Sleep(delay) for debugging purposes, but it can be more complex, doing some logging etc. 出于调试目的,它可以像Thread.Sleep(delay)一样简单,但也可以更复杂,进行一些日志记录等。

I'm choosing between a delegate and an interface with a single method, as a parameter to the constructor of the task-driving class. 我在委托和具有单个方法的接口之间进行选择,作为任务驱动类的构造函数的参数。 Which option to take? 采取哪种选择?

IMO, when it comes to DI, the major advantage of interfaces over delegates is extensibility. IMO,就DI而言,与委托相比,接口的主要优点是可扩展性。 New methods can be easily added. 可以轻松添加新方法。 I can create interface I2: I1 { ... } , have the class implement I2 , and still inject an instance of it as I1 . 我可以创建interface I2: I1 { ... } ,让类实现I2 ,并且仍将其实例注入为I1 The client code has an option to cast it to I2 , to see if the new features are supported. 客户端代码具有将其I2转换为I2的选项,以查看是否支持新功能。

However, if I need to inject just a single method, I think a delegate would make more sense, regardless of whether I need to maintain state or not. 但是,如果我只需要注入一个方法,我认为委托将更有意义,无论我是否需要维护状态。 Delegates can maintain state too, eg: 代表们也可以保持状态,例如:

static Action<TaskState> GetThrottle(int delay) 
{ 
    return (s) => Thread.Sleep(delay++);
} 

I'd explicitly type my delegate, rather than using Action<> or Func<> . 我会明确键入我的委托,而不是使用Action<>Func<>

Currently, I plan to have a separate static class with various Throttle implementations like above. 目前,我计划使用一个类似上述各种Throttle实现的单独的static类。

I'm not using any DI frameworks for this projects. 我没有为此项目使用任何DI框架。

Is this the right choice? 这是正确的选择吗? Should I go with an interface instead? 我应该改用接口吗?

If you think the answer would be mostly opinion-based, just vote to close this question, that'd help too. 如果您认为答案主要是基于观点的,只需投票关闭该问题,这也将有所帮助。

The one advantage that passing an interface to the constructor would have is it makes the dependency resolution easier to declare in a DI framework. 将接口传递给构造函数的一个好处是,它使得依赖关系解析更容易在DI框架中声明。 If a have a class like. 如果有一个类就好。

public class ClassA{
    public ClassA(IInterface interface){
    ...
    }
}

then using a DI framework like Unity I can easily register the type like so 然后使用像Unity这样的DI框架,我可以像这样轻松注册类型

 container.RegisterType<IInterface, ConcreteImplementation>();

Its a little harder to get right with delegates. 与代表相处有点困难。 you would probably have to do something like. 您可能需要做类似的事情。

public class ClassA{
    public ClassA(Delegate delegateInstance){
    ...
    }
}

then to register the dependency its a bit tougher. 然后注册依赖项会更困难。

 container.RegisterType<ClassA>(
     new InjectionFactory(a => {
         return new ClassA(()=>{/*delegate code*/});
 }));

In one of his talks, wise man David Chappell had once said that if you are facing a design problem that has a couple of approaches to solve it, and you have to decide between a shortcut or a path that is bit difficult but provides extensibility, choose the later. 在他的一次演讲中,明智的人David Chappell曾说过,如果您面临的设计问题有多种解决方法,那么您必须在一条捷径或一条艰难但可扩展的道路之间做出选择,选择稍后。 Extensibility might come in handy if needed down the road. 如果需要,可扩展性可能会派上用场。 If you are driving a car at night, there is only so far you can see as your headlights reach. 如果您在夜间开车,那么只有大灯可以看到的范围才可以看到。 As you move forward, road becomes clearer and clearer. 随着您的前进,道路变得越来越清晰。 Though this may not apply to every possible scenario out there, this advice has helped me immensely. 尽管这可能并不适用于所有可能的情况,但此建议对我有很大帮助。

In your case, if I wasn't sure that one delegate is all that I am ever going to need, I would make it extensible by using interfaces. 在您的情况下,如果我不确定一个委托就是我所需要的全部,那么我将使用接口使其可扩展。

The ability to add extra methods to an interface is a double-edged sword. 向接口添加其他方法的能力是一把双刃剑。 Several classes depend on an interface. 几个类取决于接口。 One class needs some extra dependency, and someone decides to shove it into the existing interface (and its implementation.) They shouldn't, but they do. 一类需要一些额外的依赖关系,有人决定将其推入现有接口(及其实现)中。他们不应该这样做,但确实需要。 Now the dependency has an extra method that other classes don't need, so interface segregation is violated. 现在,依赖项具有其他类不需要的额外方法,因此违反了接口隔离。 Plus the class that depends on it is doing more, but it's harder to tell because its number of dependencies hasn't increased. 加上依赖它的类正在做更多的事情,但是很难说出来,因为它的依赖项数量没有增加。

You can register delegates with DI containers. 您可以向DI容器注册代理。 It's not pretty at first, but with a few extension methods it's okay. 乍一看并不漂亮,但是有了一些扩展方法就可以了。 There may be cases where a static method is suitable, and registering a static method as the implementation of a delegate is really easy because there's no type to register or resolve, just the delegate itself. 在某些情况下,可以使用静态方法,并且将静态方法注册为委托的实现非常容易,因为没有类型可以注册或解析,而只有委托本身。

Here's an example using Autofac: 这是使用Autofac的示例:

Delegate: 代表:

public delegate Single DoMath(Single value1, Single value2);

Extension: 延期:

public static class AutofacBuilderExtensions 
{ 
    public static IRegistrationBuilder<TDelegate, SimpleActivatorData, SingleRegistrationStyle> RegisterDelegate<TDelegate, TSource>( 
        this ContainerBuilder builder,  
        Func<TSource, TDelegate> extractDelegate,  
        string sourceComponentName = null,  
        string registeredComponentName = null)  
        where TDelegate : class
    {
        var registrationFunction = new Func<IComponentContext, TDelegate>(context => 
        { 
            var c = context.Resolve<IComponentContext>(); 
            var source = sourceComponentName == null 
                ? c.Resolve<TSource>() 
                : c.ResolveNamed<TSource>(sourceComponentName); 
            return extractDelegate(source); 
        }); 

        return registeredComponentName == null ? 
            builder.Register(registrationFunction) : 
            builder.Register(registrationFunction) 
                .Named<TDelegate>(registeredComponentName); 
    } 
}

// register the type containing the method so it can be resolved,
// and then register the implementation of the delegate using the extension.
builder.RegisterType<AddsNumbers>();
builder.RegisterDelegate<DoMath, AddsNumbers>(addsNumbers => addsNumbers.DoMath);

Or if you're just registering a static implementation: 或者,如果您只是注册静态实现:

builder.Register<DoMath>(context => MyStaticMathClass.AddsNumbers);

More details, plus Windsor extensions 更多详细信息,以及Windsor扩展

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM