C# 委托vs接口与DI的单一方法
我有一项任务要做一些背景工作。我想注入C# 委托vs接口与DI的单一方法,c#,.net,interface,dependency-injection,delegates,C#,.net,Interface,Dependency Injection,Delegates,我有一项任务要做一些背景工作。我想注入voidthrottle(taskState)方法。为了调试的目的,它可以像Thread.Sleep(delay)一样简单,但也可以更复杂,比如做一些日志记录等等 我在委托和带有单个方法的接口之间进行选择,作为任务驱动类构造函数的参数选择哪个选项? 在我看来,当涉及到DI时,接口相对于委托的主要优势是可扩展性。可以很容易地添加新方法。我可以创建接口I2:I1{…},让类实现I2,并且仍然以I1的形式注入它的实例。客户端代码有一个选项可以将其强制转换为I2,以
voidthrottle(taskState)
方法。为了调试的目的,它可以像Thread.Sleep(delay)
一样简单,但也可以更复杂,比如做一些日志记录等等
我在委托和带有单个方法的接口之间进行选择,作为任务驱动类构造函数的参数选择哪个选项?
在我看来,当涉及到DI时,接口相对于委托的主要优势是可扩展性。可以很容易地添加新方法。我可以创建接口I2:I1{…}
,让类实现I2
,并且仍然以I1
的形式注入它的实例。客户端代码有一个选项可以将其强制转换为I2
,以查看是否支持新功能
然而,如果我只需要注入一个方法,我认为委托会更有意义,不管我是否需要维护状态。学员也可以保持状态,例如:
static Action<TaskState> GetThrottle(int delay)
{
return (s) => Thread.Sleep(delay++);
}
静态动作GetThrottle(int延迟)
{
返回=>Thread.Sleep(延迟++);
}
我会显式地键入我的委托,而不是使用Action
或Func
目前,我计划有一个单独的static
类,包含上述各种Throttle
实现
我没有在这个项目中使用任何DI框架
这是正确的选择吗?我应该改用界面吗
如果你认为答案大部分是基于意见的,那么投票结束这个问题也会有帮助。在他的一次演讲中,智者大卫·查佩尔曾经说过,如果你面临一个设计问题,有几种方法可以解决它,您必须在快捷方式和提供可扩展性的有点困难的路径之间做出选择,选择后者。如果将来需要,扩展性可能会派上用场。如果你在晚上开车,当你的前灯到达时,你只能看到很远的地方。随着你们的前进,道路变得越来越清晰。虽然这可能不适用于所有可能的情况,但这个建议对我帮助很大
在你的情况下,如果我不确定一个委托是我所需要的全部,我会通过使用接口使其可扩展。在他的一次演讲中,智者David Chappell曾说过,如果你面临的设计问题有两种方法可以解决,您必须在快捷方式和提供可扩展性的有点困难的路径之间做出选择,选择后者。如果将来需要,扩展性可能会派上用场。如果你在晚上开车,当你的前灯到达时,你只能看到很远的地方。随着你们的前进,道路变得越来越清晰。虽然这可能不适用于所有可能的情况,但这个建议对我帮助很大
在您的情况下,如果我不确定一个委托是我所需要的全部,我会通过使用接口使其可扩展。将接口传递给构造函数的一个优点是它使依赖项解析更容易在DI框架中声明。如果你有一个像这样的班级
public class ClassA{
public ClassA(IInterface interface){
...
}
}
然后使用像Unity这样的DI框架,我可以很容易地注册这样的类型
container.RegisterType<IInterface, ConcreteImplementation>();
那么,注册依赖项就有点难了
container.RegisterType<ClassA>(
new InjectionFactory(a => {
return new ClassA(()=>{/*delegate code*/});
}));
container.RegisterType(
新注射工厂(a=>{
返回新的ClassA(()=>{/*委托代码*/});
}));
将接口传递给构造函数的一个优点是,它使依赖项解析更容易在DI框架中声明。如果你有一个像这样的班级
public class ClassA{
public ClassA(IInterface interface){
...
}
}
然后使用像Unity这样的DI框架,我可以很容易地注册这样的类型
container.RegisterType<IInterface, ConcreteImplementation>();
那么,注册依赖项就有点难了
container.RegisterType<ClassA>(
new InjectionFactory(a => {
return new ClassA(()=>{/*delegate code*/});
}));
container.RegisterType(
新注射工厂(a=>{
返回新的ClassA(()=>{/*委托代码*/});
}));
向接口添加额外方法的能力是一把双刃剑。几个类依赖于一个接口。一个类需要一些额外的依赖项,有人决定将其放入现有接口(及其实现)中。他们不应该这样做,但他们确实这样做了。现在,依赖项有一个其他类不需要的额外方法,因此违反了接口隔离。另外,依赖它的类正在做更多的事情,但很难判断,因为它的依赖项数量没有增加
您可以使用DI容器注册代理。一开始并不漂亮,但有一些扩展方法就可以了。在某些情况下,静态方法是合适的,将静态方法注册为委托的实现非常容易,因为没有要注册或解析的类型,只有委托本身
以下是使用Autofac的示例:
代表:
public delegate Single DoMath(Single value1, Single value2);
分机:
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);
公共静态类AutofacBuilderExtensions
{
公共静态iRegistrationBuilderRegisterDelegate(
这个集装箱建造商,
Func代表,
字符串sourceComponentName=null,
字符串registeredComponentName=null)
TDelegate:类在哪里
{
变量注册函数=新函数(上下文=>
{
var c=context.Resolve();
var source=sourceComponentName==null
?c.解决()
:c.ResolveNamed(sourceComponentName);
返回代理(源);
});
返回registeredComponentName==null?
生成器注册(注册功能):
生成器注册(注册功能)
.Named(registeredComponentName);
}
}
//注册包含该方法的类型,以便对其进行解析,
//然后使用扩展注册委托的实现。
RegisterType向接口添加额外方法的能力