【问题标题】:I am new to delegates and wonder how to break down the following code我是代表的新手,想知道如何分解以下代码
【发布时间】:2018-05-25 22:06:10
【问题描述】:

我有以下代码,想知道如何分解它?它工作得很好,但是是由一位大师写的,而且在我头上。提前感谢您的帮助。

代码基本上将发送和接收的设备信息写入 GUI。似乎它具有基于设备创建的事件并且还调用 GUI 的东西。

  1. 为什么需要调用?
  2. 我想我想知道这是否过于复杂或合适?完成相同任务的更好方法是什么?
  3. 我也想知道“代表 { };”是什么有吗?

公共事件 EventHandler CommunicationPerformed = delegate { };

    SerialPort _port;
    readonly int _delay = 100;


    private string ReadAndUpdateStatus()
    {
        string read = _port.ReadExisting();
        CommunicationPerformed?.Invoke(this, new LoaderReadWriteEventArgs(read, LoaderCommunicationDirection.Read));
        return read;
    }

    private void WriteAndUpdateStatus(string data)
    {
        if (!data.StartsWith("//")) //ignore "Comment" lines but show them on the GUI to read
            _port.Write(data);
        CommunicationPerformed?.Invoke(this, new LoaderReadWriteEventArgs(data, LoaderCommunicationDirection.Write));
    }



public class LoaderReadWriteEventArgs : EventArgs
{
    public LoaderCommunicationDirection Direction { get; }

    public string Value { get; }

    public LoaderReadWriteEventArgs(string value, LoaderCommunicationDirection direction)
    {
        Value = value;
        Direction = direction;
    }
}

public enum LoaderCommunicationDirection
{
    Read,
    Write
}

【问题讨论】:

    标签: c#


    【解决方案1】:

    你问了三个问题,和往常一样,只有一个得到了回答。试着在你的问题中只问一个问题。

    我也想知道delegate { } 是做什么的?

      public event EventHandler CommunicationPerformed = delegate { };
    

    正如其他答案所述,事件在 C# 中默认为空。这种技术使事件处理程序什么都不做,但不是null

    不幸的是,C# 中有许多用于匿名函数的语法。 delegate {} 的意思是“给我一个无所事事的函数,它匹配任何返回无效的非引用形式参数列表”。这就是人们使用delegate{} 的原因,因为它几乎可以在需要事件处理程序的上下文中的任何地方工作。

    【讨论】:

      【解决方案2】:

      我认为您需要查看此https://docs.microsoft.com/en-us/dotnet/standard/events/

      为什么需要调用?

      向事件处理程序广播(注册此事件的任何一方都会以这种方式得到通知)

      我想我想知道这是否过于复杂或合适?

      *完成相同任务的更好方法是什么?

      这并不复杂。这非常简单。*

      我也想知道“代表{};”是什么有吗?

      我的两分钱。 我不知道您指的是哪个代码行,但委托是一种包含对方法的引用的类型。委托使用签名声明,该签名显示其引用的方法的返回类型和参数,并且只能保存对与其签名匹配的方法的引用。因此,委托等同于类型安全的函数指针或回调。一个委托声明足以定义一个委托类。

      【讨论】:

        【解决方案3】:

        orhtej2 很好地回答了您的第一个问题。使用显式 Invoke 方法允许您利用 null 条件运算符,从而将触发事件的代码减少到一行。

        至于这是否过于复杂:不,基本上事件是这样完成的。

        我有时看到的(尤其是与 MVVM 模式中的 INotifyPropertyChanged 接口结合使用)是帮助方法,它封装了更长但更明显的 C#6 之前的代码,并让您以更少的管道触发事件。

        private void FireCommPerformed(string value, LoaderCommunicationDirection direction) 
        { 
            EventHandler handler = CommunicationPerformed;
            if (handler != null)
            {
                handler(this, new LoaderReadWriteEventArgs(value, direction)));
            }
        }
        

        你可以这样使用它:

        private string ReadAndUpdateStatus()
        {
            string read = _port.ReadExisting();
            FireCommPerformed(read, LoaderCommunicationDirection.Read);
            return read;
        }
        

        Delegate { } 只是一个空的委托,就像事件的 null 一样。除非代码中的其他方法在运行时订阅了该类的 CommunicationPerformed 事件,否则触发该事件时不会发生任何事情。

        【讨论】:

          【解决方案4】:

          让我们专注于这里到底发生了什么。这应该可以帮助您弄清楚其他所有问题。

          CommunicationPerformed?.Invoke(this, new LoaderReadWriteEventArgs(read, LoaderCommunicationDirection.Read));
          

          这是检查委托(事件处理程序是委托)(CommunicationPerformed)是否为空。

          委托是指针(但具有额外的功能)。

          如果委托(指针)为空,则表示没有分配任何内容。

          指针存储内存位置。在这种情况下,它对函数“this”进行内存引用。 'This' 是对 ReadAndUpdateStatus() 的引用。

          所以这一行基本上是说:“由于指针为空,所以让这个指针引用 ReadAndUpdateStatus() 函数”。

          但是事件参数呢?嗯...这就是委托与指针不同的地方。

          委托不仅仅安全地保存和存储内存位置。它们还可以保存参数。

          您使用从 EventArgs 扩展的类作为传入参数列表的方式。

          从这里开始,事件处理程序(事件处理程序是委托)CommunicationPerformed 将协调将该参数列表发送到任何需要它的函数。

          只要调用 CommunicationPerformed(例如,告诉运行),就会调用这些函数。这通常用以下符号表示:

          +=CommunicationPerformed(foo,bar) 
          

          现在 - 为什么要使用事件处理程序(或任何委托 - 就此而言)?

          它们冗长且读起来很烦人(比编写简单的布尔函数和触发函数要多得多),它们看起来不像其他函数,而且坦率地说它们很奇怪 - 对吧?

          除了它们真的很有用。方法如下:

          1.) 它们的工作方式与任务非常相似。您可以在任何地方并行、循环调用它们。他们保持一致的状态。它们不会“流血”并导致错误。

          2.) 它们是指针。保证通过引用。除了,它们很神奇,如果你停止使用委托,它们就不会留在记忆中。

          3.) 它们允许您在循环中控制状态。有时,如果您处于非常紧密的循环中,则布尔触发器将无法正常工作。你触发一个事件?保证您的触发器只会被触发一次的行为。

          【讨论】:

            【解决方案5】:

            我将尝试回答 #1 和 #2(3 已经涵盖)。

            #1 - 在某个时候,有人决定程序的其他部分可以订阅一个事件,告诉他们何时执行了通信。一旦做出决定,它就是一种“合同”。您执行通信->您触发通知订阅者已执行通信的事件。那些订阅者对此做了什么,或者他们为什么需要知道......如果这门课是你的重点,实际上你并不关心。理论上,至少。通常情况确实如此。如果您的班级正在完成其工作,那么您真正关心的是谁在听这些事件。

            #2 - 我确实认为在代码中声明事件和事件处理程序的方法过于复杂。很多人(以及官方Microsoft best practices)不同意我的观点。您可以谷歌“为什么我的事件处理程序应该使用 eventargs”并阅读大量有关该主题的内容。 Or Look here。另一种方法如下:

            public event Action<string, LoaderCommunicationDirection> CommunicationPerformed;
            void PerformWrite()
            {
              string myComm = "String I'm sending";
              //Line of code that performs communication that writes string here
              CommunicationPerformed?.Invoke(myComm, LoaderCommunicationDirection.Write);
            }
            

            这比从EventArgs 派生的整个类要简洁得多。但是,它有一个非常明显的缺点,如果您是事件订阅者……您不知道string 是什么。当然,因为它在您的代码中被命名为value......这并没有多大帮助。事件声明上方的注释也同样有用。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2022-06-23
              • 1970-01-01
              • 1970-01-01
              • 2021-05-25
              • 1970-01-01
              • 2018-08-19
              相关资源
              最近更新 更多