事件和代表与调用方法

Tez*_*eld 4 c#

我希望这个问题不与其他问题密切相关,但其他问题似乎并没有填补知识空白.

这似乎是尝试和理解事件和代表的热门话题,在阅读了许多SO问题和MSDN文章后,我不敢说我​​仍然不明白.经过几年创建出色的Web应用程序,我发现自己因不理解它们而感到非常沮丧.请允许任何人在通用代码中澄清这一点.所以问题是,为什么你会使用事件和委托来调用方法?

下面是我在工作中编写的一些基本代码.我可以利用活动和代表吗?

Public Class Email
{
   public string To {get;set;}
   //Omitted code

   public void Send()
   {
    //Omitted code that sends.
   }
}

Public Class SomeClass
{
   //Some props

   Public void DoWork()
  {
    //Code that does some magic

    //Now Send Email
    Email newEmail = new Email();
    newEmail.To = "me@me.com";
    newEmail.Send();
  }
}
Run Code Online (Sandbox Code Playgroud)

这可能不是最好的例子,但无论如何DoWork()方法可以订阅电子邮件吗?这会有用吗?任何有助于我真正理解事件和代表的帮助将不胜感激.

问候,

Pau*_*fin 7

我在实际编程中发现的使用事件和委托的最大原因是简化了代码维护鼓励代码重用的任务.

当一个类调用另一个类中的方法时,这些类是"紧耦合"的.你紧密耦合的课程越多,改变其中一个课程就越困难,而不必改变其他课程.你可以在那时写一个大班.

使用事件可以使事物更"松散耦合",并且可以更轻松地更改一个类而不必打扰其他类.

以上面的例子为例,假设我们有第三个类,Logger应该在发送电子邮件时记录.它使用一种方法,LogEvent(string desc, DateTime time)将条目写入日志:

public class Logger 
{
  ...
  public void LogEvent(string desc, DateTime time)
  {
    ...//some sort of logging happens here
  }
}
Run Code Online (Sandbox Code Playgroud)

如果我们使用方法,我们需要更新你的Email类' Send方法来实例化a Logger并调用它的LogEvent方法:

public void Send()
   {
    //Omitted code that sends.
    var logger = new Logger();
    logger.LogEvent("Sent message", DateTime.Now);
   }
Run Code Online (Sandbox Code Playgroud)

现在Email是紧密耦合的Logger.如果我们更改了该LogEvent方法的签名Logger,我们还必须对其进行更改Email.当你处理一个中型项目时,你是否看到这会如何迅速成为一场噩梦?此外,没有人愿意甚至尝试使用该LogEvent方法,因为他们知道如果他们需要对其进行任何改变,他们将不得不开始改变其他类,并且应该在下午的工作中迅速变成一周.所以相反,他们编写了一个新方法,或者一个新类,然后紧密耦合到他们正在做的任何其他事情,事情变得臃肿,每个程序员开始进入他们自己的代码中的小"贫民窟".当你必须稍后进入并弄清楚程序正在做什么或追捕一个bug时,这非常非常糟糕.

如果你在Email课堂上放一些事件,你可以松散地结合这些课程:

Public Class Email
{
   public event EventHandler<EventArgs> Sent;
   private void OnSent(EventArgs e)
    {
        if (Sent!= null)
            Sent(this, e);
    }

   public string To {get;set;}
   //Omitted code

   public void Send()
   {
    //Omitted code that sends.
    OnSent(new EventArgs());//raise the event
   }
}
Run Code Online (Sandbox Code Playgroud)

现在,您可以在应用程序的任何位置添加事件处理程序Logger并将其归档到Email.Sent事件中,并让它执行它需要执行的操作:

public class Logger 
{
  ...
  public void Email_OnSent(object sender, EventArgs e)
  {
    LogEvent("Message Sent", DateTime.Now);
  }

  public void LogEvent(string desc, DateTime time)
  {
    ...//some sort of logging happens here
  }
}
Run Code Online (Sandbox Code Playgroud)

和其他地方:

var logger = new Logger();
var email = new Email();

email.Sent += logger.Email_OnSent;//subscribe to the event
Run Code Online (Sandbox Code Playgroud)

现在你的课程非常宽松,六个月后,当你决定要Logger捕获更多或不同的信息,甚至在发送电子邮件时做一些完全不同的事情时,你可以改变LogEvent方法或事件处理程序无需接触Email课程.此外,其他类也可以订阅事件而无需更改Email类,并且您可以在发送电子邮件时发生大量事情.

现在维护代码要容易得多,而其他人更有可能重用代码,因为他们知道他们不必去挖掘20个不同类的内容,只是为了改变处理方式.

大编辑:有关代表的更多信息.如果你在这里阅读:好奇心是幸福:C#事件与代表(我保持链接到最低限度,我保证),你看到作者如何进入事件基本上是特殊类型的代表这一事实.他们期望某个方法签名(即(object sender, EventArgs e)),并且可以在+=引发方法时添加多个方法().还有其他差异,但这些是你会注意到的主要差异.代表有什么好处?

想象一下,您想为您的Email班级客户提供一些如何发送邮件的选项.您可以为此定义一系列方法:

Public Class Email
{
   public string To {get;set;}
   //Omitted code

   public void Send(MailMethod method)
   {
     switch(method)
     {
       case MailMethod.Imap:
         ViaImap();
         break;
       case MailMethod.Pop:
         ViaPop();
         break;
      }
   }

   private void ViaImap() {...}

   private void ViaPop() {...}
}
Run Code Online (Sandbox Code Playgroud)

这很有效,但如果您想稍后添加更多选项,则必须编辑您的类(以及MailMethod此处假设的枚举).如果您改为声明委托,则可以将此类决定推迟到客户端并使您的类更灵活:

Public Class Email
{
   public Email()
   {
     Method = ViaPop;//declare the default method on instantiation
   }

   //define the delegate
   public delegate void SendMailMethod(string title, string message);

   //declare a variable of type SendMailMethod
   public SendMailMethod Method;

   public string To {get;set;}
   //Omitted code

   public void Send()
   {
     //assume title and message strings have been determined already
     Method(title, message);
   }

   public void SetToPop()
   {
     this.Method = ViaPop;
   }

   public void SetToImap()
   {
     this.Method = ViaImap;
   }

   //You can write some default methods that you forsee being needed
   private void ViaImap(string title, string message) {...}

   private void ViaPop(string title, string message) {...}
}
Run Code Online (Sandbox Code Playgroud)

现在,客户端可以使用您自己的方法使用您的类,或者提供自己的方法来发送邮件,但是他们选择:

var regularEmail = new Email();
regularEmail.SetToImap();
regularEmail.Send();

var reallySlowEmail = new Email();
reallySlowEmail.Method = ViaSnailMail;

public void ViaSnailMail(string title, string message) {...}
Run Code Online (Sandbox Code Playgroud)

现在你的类有点紧密耦合,更容易维护(并为其编写测试!).当然还有其他方法可以使用代表,而lambdas则可以将事情提升一个档次,但这应该足以引起一些简单的介绍.