请问有人能够解释在C#中使用Action<T>
和Predicate<T>
委托的确切原因是什么?
你是在问它们存在的原因,还是在问它们为什么被定义为委托?
至于它们存在的原因,可能最好的原因就是方便。如果你想要一个返回无值并且参数为某种类型的委托,那么你可以自己定义:
public delegate void MyDelegate(int size);
然后稍后创建一个:
MyDelegate proc = new MyDelegate((s) => { // do stuff here });
当然,你需要针对每种不同的类型编写这样的方法。
或者,你可以使用Action<T>
:
Action<int> proc = new Action<int>((s) => { /* do stuff here */ });
当然,你可以简化为:
Action<int> proc = (s) => { /* do stuff here */ });
为什么它们是委托?因为这是在.NET中操作函数引用的方式:我们使用委托。注意上面例子中的相似之处。也就是说,MyDelegate
在概念上与 Action<int>
相同。虽然它们不是完全相同的东西,因为它们具有不同的类型,但是你可以轻松地将程序中每个 MyDelegate
的实例替换为 Action<int>
,并且程序将正常工作。
它们还能是什么呢?委托是最灵活、最接近Action<T>
、Predicate<T>
和Func<T>
所建模的内容 - 具有指定参数类型(也称为委托)的调用列表。
Action<T>
和 Predicate<T>
委托来执行操作和谓词操作的习语之所以被选择,最好的解释是先考虑替代方案。如果没有委托,你如何完成同样的事情?下一个最好的方案是使用接口 IAction
和 IPredicate
并强制开发人员声明一个类并为每个不同类型的需要操作实现适当的接口。但是,这种方法的问题在于它需要更多的努力、更多的类扩散和不太可维护的代码。委托方法更加方便,因为您可以使用匿名方法或 lambda 表达式将逻辑与其将被使用的地方写在一起。IAction<T>
不能有一个属性,使得当编译器看到将方法组分配给IAction<Fnord>
时,它将检查IAction<Fnord>
是否包含一个符合消息组的签名的Invoke
方法,并且如果是这样,生成一个类来实现IAction<Fnord>.Invoke
作为对适当方法的调用。 - supercatIDisposable
。如果枚举器也实现了IDisposable
,则foreach
结构将调用Dispose
。yield
关键字和IEnumerator
的交互是另一个例子。我相信还有更多。所以,是的,你提出的建议几乎肯定是可行的。 - Brian GideonIInvoke
接口(所以对于Action<Fnord,Woozle>
,接口将是Action<Fnord,Woozle>.IInvoke
);如果代码在实际使用时使用接口类型,那么这将提供最佳的解决方案。顺便说一句,我认为在委托类型中包含“BeginInvoke”很奇怪;我认为包含一个Bind
方法会更有帮助... - supercatMethodInvoker
。例如,如果Act
是一个Action<int, string>
,那么Act.Bind(5,"Hello")
将生成一个MethodInvoker
,该MethodInvoker
将调用Act(5,"Hello")
。据我所知,BeginInvoke
必须将委托及其参数绑定到单个对象中,然后将该对象放置在线程池中;而不是将此类参数绑定功能绑定到线程池中,应将其单独公开。除其他外,这将允许像Control.BeginInvoke
这样的东西具有类型安全性。 - supercat你从错误的角度看待这个问题。
问题不应该是“为什么需要 Action<>
、Func<>
和 Predicate<>
委托?”(答案是“因为它们需要”)
真正的问题是,“为什么我们需要特定命名的对象来处理简单的委托任务?在可以使用普通委托的地方为什么要使用 Action<>
?”
答案是,这些特定的委托在 CLR 本身上经常被使用,由于它们在 CLR 方法中被使用,最终开发人员将必须定义被调用的方法,委托的声明必须是公共的。因此,Microsoft 可以定义成千上万个委托,每个委托只用于一个 CLR 方法,或者定义一种简单的方式来指定需要哪种类型的委托。
Func<T>
呢? - Oded