为什么我们需要C#代理

时间:2010-11-26 10:34:49

标签: c# delegates

我似乎永远不明白我们为什么需要代表? 我知道它们是包含方法引用的不可变引用类型,但为什么我们不能直接调用该方法,而不是通过委托调用它?

由于

8 个答案:

答案 0 :(得分:59)

简单回答:需要执行操作的代码不知道写入时要调用的方法。如果你在编译时知道要调用哪种方法,你只能直接调用该方法,对吧?因此,如果您想要抽象出“在适当的时间执行操作X”的想法,您需要对操作进行一些表示,以便调用操作的方法不需要提前知道确切的实现。

例如:

    LINQ中的
  • Enumerable.Select无法知道您要使用的投影,除非您告诉它
  • Button的作者不知道希望用户点击它时的操作
  • 如果新线程只做了一件事,那就太无聊了......

它可能会帮助您将委托视为单方法接口,但使用大量语言语法使其易于使用,并且支持异步执行和多播。

答案 1 :(得分:33)

当然,您可以直接在对象上调用方法,但请考虑以下方案:

  1. 您希望通过使用单个委托调用一系列方法,而无需编写大量方法调用。
  2. 您希望优雅地实现基于事件的系统。
  3. 您希望在签名中调用两个相同但位于不同类中的方法。
  4. 您希望将方法作为参数传递。
  5. 您不想像在LINQ中那样编写大量的多态代码,您可以为Select方法提供大量实现。

答案 2 :(得分:17)

因为您可能已经编写了方法,或者您已经设计了您的类,其用户可以决定用户希望您的类使用哪种方法(该用户编写)执行。

它们还使某些设计更干净(例如,代替您调用不同方法的switch语句,调用传入的委托)并且更容易理解并允许扩展代码而不更改它(想想OCP)。 / p>

代表也是事件系统的基础 - 在没有代表的情况下编写和注册事件处理程序会比使用它们更难。

查看Linq中的不同ActionFunc代表 - 如果没有它们,它几乎没有用。

话虽如此,没有人强迫你使用代表。

答案 3 :(得分:8)

  1. 代表支持活动
  2. 代表为程序提供了一种执行方法的方法,而无需在编译时准确知道这些方法是什么

答案 4 :(得分:4)

代表们可以在没有他们的情况下完成任何事情,但代表们提供了更清晰的方法。如果没有委托,则必须为包含函数Invoke(适当的参数)的每个可能的函数签名定义接口或抽象基类,并为每个可由伪委托调用的函数定义一个类。该类将继承函数签名的适当接口,包含对包含它应该表示的函数的类的引用,以及实现Invoke(适当的参数)的方法,该方法将调用它所持有的类中的相应函数一个参考。如果类Foo有两个方法Foo1和Foo2,两个方法都采用一个参数,两个都可以被伪委托调用,那么就会创建两个额外的类,每个方法一个。

如果没有编译器支持这种技术,源代码必须非常令人发指。但是,如果编译器可以自动生成正确的嵌套类,那么事情可能非常简洁。伪委托的调度速度通常可能比传统委托慢,但如果伪委托是接口而不是抽象基类,那么只需要为给定签名的一个方法创建伪委托的类就可以了。实现适当的伪委托接口本身;然后可以将类实例传递给期望该签名的伪委托的任何代码,从而避免创建额外对象的任何需要。此外,虽然使用伪委托时所需的类数量大于使用“真实”委托时所需的类数,但每个伪委托只需要保存一个对象实例。

答案 5 :(得分:3)

考虑C / C ++函数指针,以及如何将javascript事件处理函数视为“数据”并传递它们。在Delphi语言中也有程序类型。 在幕后,C#delegate和lambda表达式以及所有这些基本相同的想法:代码作为数据。这构成了函数式编程的基础。

答案 6 :(得分:1)

你问了一个例子,说明为什么你要把一个函数作为一个参数传递,我有一个完美的函数,并认为它可能有助于你理解,它是相当学术的,但显示了一个用途。假设您有一个ListResults()方法和getFromCache()方法。如果缓存为空,则可以进行大量检查。您可以将任何方法传递给getCache,然后只有在getFromCache方法中缓存为空时才调用它:

_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;
    }

答案 7 :(得分:0)

您可以将它们视为与C / C ++中的函数指针类似的构造。但它们不仅仅是C#中的。 Details