2

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

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

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

公共事件 EventHandler CommunicationPerformed = 委托 { };

    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
}
4

5 回答 5

4

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

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

  public event EventHandler CommunicationPerformed = delegate { };

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

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

于 2018-05-25T22:20:14.770 回答
1

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

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

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

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在您的代码中命名的......这并没有多大帮助。事件声明上方的注释也同样有用。

于 2018-05-25T22:31:08.453 回答
1

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 事件,否则触发该事件时不会发生任何事情。

于 2018-05-25T22:34:24.227 回答
1

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

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

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

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

如果委托(指针)为空,则意味着没有分配给它。

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

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

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

代表不只是安全地保存和存储内存位置。它们还可以保存参数。

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

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

每当调用 CommunicationPerformed(例如被告知运行)时,都会调用这些函数。这通常用以下符号表示:

+=CommunicationPerformed(foo,bar) 

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

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

除了它们真的很有用。就是这样:

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

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

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

于 2018-05-25T22:57:21.213 回答
1

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

为什么需要调用?

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

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

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

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

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

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

于 2018-05-25T22:24:11.337 回答