为什么我们需要C#代表
我似乎从未理解为什么我们需要代表? 我知道它们是持有引用的方法的不可变引用types,但为什么我们不能直接调用方法,而不是通过委托调用它?
谢谢
当然,你可以直接调用对象的方法,但考虑以下情况:
- 你想用一个委托调用一系列的方法而不用写很多的方法调用。
- 你想优雅地实现基于事件的系统。
- 你想调用两个相同的签名方法,但驻留在不同的类。
- 你想传递方法作为参数。
- 你不想像LINQ那样编写大量的多态代码,你可以为
Select
方法提供大量的实现。
简单的回答:需要执行操作的代码在写入时不知道调用的方法。 你只能直接调用方法,如果你知道在编译时调用哪个方法,对吧? 因此,如果要抽象出“在适当的时候执行动作X”的想法,则需要对动作进行一些表示,以便调用动作的方法不需要提前知道确切的实现。
例如:
- 在LINQ中
Enumerable.Select
不能知道你想要使用的投影,除非你告诉它 -
Button
的作者不知道当用户点击它时你想要的动作是什么 - 如果一个新的线程只做了一件事,这将是无聊的…
它可以帮助你把代表想象成单一方法的接口,但是有很多语言语法使它们易于使用,并且支持asynchronous执行和多播。
因为你可能还没有写出方法,或者你已经devise了你的类,使得用户可以决定用户想要你的类执行什么方法(用户编写)。
它们还使得某些devise更加清晰(例如,您可以调用不同的方法来代替switch语句,您可以调用传入的委托),并且更容易理解并允许扩展代码,而不用更改代码(请考虑OCP)。
代表也是事件系统的基础 – 没有代表的写作和注册事件处理程序将比它们更难。
看看Linq中不同的Action
和Func
代表 – 没有他们,这将不会有用。
话虽如此,没有人强迫你使用代表。
- 代表支持事件
- 委托给你的程序一个方法来执行方法,而不必精确知道这些方法在编译时是什么
想想C / C ++函数指针,以及如何将javascript事件处理函数视为“数据”并传递给它们。 在Delphi语言中也有程序types。 在幕后,C#委托和lambdaexpression式,以及所有这些东西本质上是相同的想法:代码作为数据。 这构成了函数式编程的基础。
代表们可以做的任何事情都可以在没有他们的情况下完成 ,但是代表们提供了一个更加干净的方式。 如果没有委托,就必须为包含一个函数Invoke(适当的参数)的每个可能的函数签名定义一个接口或抽象基类,并为伪代表可调用的每个函数定义一个类。 该类将inheritance函数签名的适当接口,包含对包含它应该表示的函数的类的引用,以及实现Invoke(适当的参数)的方法,该方法将调用它所持有的类中的相应函数一个参考。 如果Foo类有两个方法Foo1和Foo2,都采用一个参数,这两个参数都可以通过伪代理来调用,那么会创build两个额外的类,每个方法一个。
如果没有编译器支持这种技术,源代码将是非常可恶的。 如果编译器可以自动生成正确的嵌套类,那么事情可能会非常干净。 伪委托的调度速度通常可能比传统委托的速度慢,但是如果伪委托是一个接口而不是抽象的基类,那么只需要为一个给定的签名的一个方法做一个伪委托的类就可以实现适当的伪代理接口本身; 然后可以将类实例传递给期望该签名的伪委托的任何代码,从而避免需要创build额外的对象。 此外,尽pipe使用伪委托时需要的类的数量将大于使用“真实”委托时的类的数量,但是每个伪委托只需要保持单个对象实例。
你问了一个例子,你为什么要传递一个函数作为一个参数,我有一个完美的,认为它可以帮助你理解,这是非常学术,但显示一个用途。 假设你有一个ListResults()方法和getFromCache()方法。 相反,如果caching为空等,则可以进行大量的检查。只要将任何方法传递给getCache,然后仅在getFromCache方法内的caching为空时调用它:
_cacher.GetFromCache(delegate { return ListResults(); }, "ListResults");
public IEnumerable<T> GetFromCache(MethodForCache item, string key, int minutesToCache = 5) { var cache = _cacheProvider.GetCachedItem(key); //you could even have a UseCache bool here for central control if (cache == null) { //you could put timings, logging etc. here instead of all over your code cache = item.Invoke(); _cacheProvider.AddCachedItem(cache, key, minutesToCache); } return cache; }
你可以把它们看作一个类似于C / C ++函数指针的构造。 但是他们比C#中的更多。 细节 。