使用代表的好处?

时间:2008-11-19 18:08:14

标签: .net design-patterns delegates

我希望在VB.NET或C#或其他一些.NET语言中实现Observer模式。我听说代理可以用于此,但无法弄清楚为什么它们比观察者实现的普通旧接口更受欢迎。所以,

  • 为什么我应该使用委托而不是定义我自己的接口并传递对实现它们的对象的引用?
  • 为什么我可以避免使用委托,并使用良好的ol-fashioned界面?

14 个答案:

答案 0 :(得分:27)

当您可以直接调用方法时,需要委托。

当调用方法的代码不知道/关心它调用的方法时,委托很有用 - 例如,您可以调用长时间运行的任务并将其传递给委托一个回调方法,该任务可用于发送有关其状态的通知。

这是一个(非常愚蠢的)代码示例:

enum TaskStatus
{
   Started,
   StillProcessing,
   Finished
}

delegate void CallbackDelegate(Task t, TaskStatus status);

class Task
{
    public void Start(CallbackDelegate callback)
    {
        callback(this, TaskStatus.Started);

        // calculate PI to 1 billion digits
        for (...)
        {
            callback(this, TaskStatus.StillProcessing);
        }

        callback(this, TaskStatus.Finished);
    }
}

class Program
{
    static void Main(string[] args)
    {
        Task t = new Task();
        t.Start(new CallbackDelegate(MyCallbackMethod));
    }

    static void MyCallbackMethod(Task t, TaskStatus status)
    {
        Console.WriteLine("The task status is {0}", status);
    }
}

正如您所看到的,Task类不知道或不关心 - 在这种情况下 - 委托是一个方法,它将任务的状态打印到控制台。该方法同样可以通过网络连接将状态发送到另一台计算机。等

答案 1 :(得分:22)

你是O / S,我是一个应用程序。当你发现发生的事情时,我想告诉你调用我的一种方法。为此,我向你传递一个代表我想要你打电话给我的方法。我自己也不称之为我的那种方法,因为我希望你在发现某事时给它打电话。你不直接调用我的方法,因为你不知道(在编译时)该方法存在(我甚至没有在你建立时编写);相反,您可以调用在运行时收到的委托指定的任何方法。

答案 2 :(得分:9)

从技术上讲,您不必使用委托(除非使用event handlers,否则它是必需的)。你可以没有他们。实际上,它们只是工具箱中的另一个工具。

关于使用它们的第一件事就是Inversion Of Control。每当你想控制一个函数在其外部的行为时,最简单的方法是将一个委托作为一个参数,让它执行委托。

答案 3 :(得分:6)

你不是在想程序员。

问题是,为什么要直接调用函数when you could call a delegate

  

David Wheeler的着名格言   去:计算机科学的所有问题   可以通过另一个层次来解决   间接。

我有点舌头。显然,您将在大多数时间直接调用函数,尤其是在模块中。但是,当需要在包含对象不可用(或相关)的上下文中调用函数时,委托是有用的,例如事件回调。

答案 4 :(得分:4)

您可以在Observer模式中使用两个代理。由于我不确定你指的是哪一个,我会尽力回答这两个问题。

第一种是使用主题中的委托而不是IObservers列表。这种方法在处理多播方面似乎更加清晰,因为你基本上已经

private delegate void UpdateHandler(string message);
private UpdateHandler Update;

public void Register(IObserver observer)
{
    Update+=observer.Update;
}

public void Unregister(IObserver observer)
{
    Update-=observer.Update;
}

public void Notify(string message)
{
    Update(message);
}

而不是

public Subject()
{
    observers = new List<IObserver>();
}

public void Register(IObserver observer)
{
    observers.Add(observer);
}

public void Unregister(IObserver observer)
{
    observers.Remove(observer);
}

public void Notify(string message)
{
    // call update method for every observer
    foreach (IObserver observer in observers)
    {
        observer.Update(message);
    }
}

除非你需要做一些特别的事情并且需要引用整个IObserver对象,否则我认为代表会更干净。

第二种情况是使用pass delegates而不是IObervers,例如

public delegate void UpdateHandler(string message);
private UpdateHandler Update;

public void Register(UpdateHandler observerRoutine)
{
    Update+=observerRoutine;
}

public void Unregister(UpdateHandler observerRoutine)
{
    Update-=observerRoutine;
}

public void Notify(string message)
{
    Update(message);
}

有了这个,观察者不需要实现一个接口。你甚至可以传入一个lambda表达式。控制水平的这种变化几乎就是差异。这是好还是坏取决于你。

答案 5 :(得分:2)

委托实际上是传递对方法的引用,而不是对象......接口是对对象实现的方法子集的引用...

如果在应用程序的某个组件中需要访问对象的多个方法,则定义表示对象方法子集的接口,并在可能需要的所有类上分配和实现该接口传递给这个组件......然后通过该接口传递这些类的实例,而不是通过它们的具体类传递..

如果,otoh,在某些方法或组件中,您只需要几种方法中的一种,它们可以在任意数量的不同类中,但都具有相同的签名,那么您需要使用委托。

答案 6 :(得分:2)

我正在重复我给this question的答案。

我一直很喜欢广播电台的比喻。

当广播电台想播放某些内容时,它会将其发送出去。它不需要知道是否有任何人在那里听。你的收音机能够通过无线电台注册(通过用拨号调入),所有无线电台广播(我们的小隐喻中的事件)都由收音机接收,并将其转换成声音。

没有这种注册(或事件)机制。无线电台必须依次联系每个无线电台并询问是否需要广播,如果你的电台说是,则将信号直接发送给它。

您的代码可能遵循非常类似的范例,其中一个类执行操作,但该类可能不知道,或者可能不想知道谁将关心或采取行动。因此,它为任何对象提供了一种方式来注册或取消注册自己以通知操作已发生。

答案 7 :(得分:2)

代表是函数/方法接口的强类型。

如果您的语言采取强键入的位置,并且它具有一流的功能(C#都具有这两种功能),那么会有代表就不一致。< / p>

考虑采用委托的任何方法。如果你没有代表,你会如何传递一些东西呢?被调用者如何对其类型有任何保证?

答案 8 :(得分:1)

我听过一些“事件福音传道者”谈到这一点,他们说,随着事件越多,事件越多,就越好。

优选地,事件源永远不应该知道事件监听器,并且事件监听器永远不应该关心谁发起了事件。这不是今天的事情,因为在事件监听器中,您通常会收到事件的源对象。

说到这里,代表们是完成这项工作的完美工具。它们允许事件源和事件观察器之间的分离,因为事件源不需要保留所有观察者对象的列表。它只保留观察者的“函数指针”(委托)列表。 因此,我认为这比Interfaces更有优势。

答案 9 :(得分:1)

从另一个角度看待它。使用自定义界面有什么好处,而不是使用语法和库中语言支持的标准方式?

当然, 的情况下,定制解决方案可能具有优势,在这种情况下,您应该使用它。在所有其他情况下,请使用最常规的解决方案。它的工作量更少,更直观(因为它是用户期望的),得到了工具(包括IDE)的更多支持,而且机会很多,编译器对它们的处理方式也不同,从而产生更高效的代码。

不要重新发明轮子(除非当前版本被破坏)。

答案 10 :(得分:1)

事实上,Sun和微软之间就代表们进行了一次有趣的反复讨论。虽然Sun对代表们采取了相当强硬的立场,但我觉得微软在使用代表方面做得更加强烈。以下是帖子:

http://java.sun.com/docs/white/delegates.html

http://msdn.microsoft.com/en-us/vjsharp/bb188664.aspx

我想你会发现这些有趣的阅读......

答案 11 :(得分:0)

我认为它与合成糖和组织代码的方式更相关,一个很好的用途是处理与公共上下文相关的几个方法,这些方法属于一个对象或静态类。

并不是你被迫使用它们,你可以使用和不使用它们进行编程,但是使用它们可能会影响它们的组织性,可读性以及为什么代码不会很酷,也许会让你感到厌烦。代码。

这里给出的每个例子都是一个很好的例子,你可以实现它们,就像有人说的那样,它只是你可以使用的语言中的另一个特性。

问候

答案 12 :(得分:0)

这是我可以写下来作为使用委托的原因。 以下代码是用C#编写的,请按照注释进行操作。

public delegate string TestDelegate();
protected void Page_Load(object sender, EventArgs e)
{
    TestDelegate TD1 = new TestDelegate(DiaplayMethodD1);
    TestDelegate TD2 = new TestDelegate(DiaplayMethodD2);
    TD2 = TD1 + TD2; // Make TD2 as multi-cast delegate
    lblDisplay.Text =  TD1(); // invoke delegate
    lblAnotherDisplay.Text = TD2();


    // Note: Using a delegate allows the programmer to encapsulate a reference 
    //       to a method inside a delegate object. Its like the function pointer
    //       in C or C++.    
}
//the Signature has to be same.
public string DiaplayMethodD1()
{
    //lblDisplay.Text = "Multi-Cast Delegate on EXECUTION"; // Enable on multi-cast 
    return "This is returned from the first method of delegate explanation";
}
// The Method can be static also
public static string DiaplayMethodD2()
{
    return " Extra words from second method";
}

最诚挚的问候, Pritom Nandy, 孟加拉

答案 13 :(得分:0)

这是一个可能有帮助的例子。

有一个应用程序使用大量数据。需要一个允许过滤数据的功能。可以指定6种不同的过滤器。

立即想到创建6种不同的方法,每种方法都返回过滤的数据。例如

public Data FilterByAge(int age)

public Data FilterBySize(int size)

....等等。

这很好但是非常有限并且产生垃圾代码,因为它已经关闭以进行扩展。

更好的方法是使用单个Filter方法并传递有关如何过滤数据的信息。这是代表可以使用的地方。委托是一个可以应用于数据以过滤它的函数。

公共数据过滤器(动作过滤器)

然后使用它的代码变为

过滤器(data =&gt; data.age&gt; 30);

过滤(data =&gt; data.size = 19);

代码数据=&gt; blah blah成为代表。代码变得更加灵活并保持开放。