如何将变量传递给 Lambda 表达式

How to pass a Variable to Lambda Expression

我上周才开始使用 C#,并尝试创建一个简单的串行监视器程序。我希望我的程序连续从串行端口读取数据,同时在表单应用程序中显示这些数据。 我使用这里的代码作为参考 https://github.com/ahelsayd/Serial-Lab

我使用相同的 BeginInvoke() 函数。但是我不能传递我想写的变量。

这是原代码

private void rx_data_event(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (mySerial.IsOpen)
            {
                try
                {
                    //=====Only this part is different=======================
                    int dataLength = mySerial.BytesToRead;
                    byte[] dataReceived = new byte[dataLength];
                    int nbytes = mySerial.Read(dataReceived, 0, dataLength);
                    if (nbytes == 0) return;
                    //=====Only this part is different=======================

                    this.BeginInvoke((Action)(() =>
                    {
                        data = System.Text.Encoding.Default.GetString(dataReceived);

                        if (!backgroundWorker1.IsBusy)
                        {
                            backgroundWorker1.RunWorkerAsync();
                        }
                    }));

                }
                catch { alert("Can't read form  " + mySerial.PortName + " port it might be opennd in another program"); }
            }
        }


//And then update the UI
private void update_rxtextarea_event(object sender, DoWorkEventArgs e)
        {
            this.BeginInvoke((Action)(() =>
            {
                if (rx_textarea.Lines.Count() > 5000)
                    rx_textarea.ResetText();
                rx_textarea.AppendText("[RX]> " + data);
            }));
        }

这段代码可以同时读取串口和写入窗体。但是,它不会从串行端口接收所有数据。所以我修改了代码,先将数据写入缓冲区,直到接收到所有数据。

修改后的代码

private void rx_data_event(object sender, System.IO.Ports.SerialDataReceivedEventArgs e)
        {
            if (mySerial.IsOpen)
            {
                try
                {
                    //=====Only this part is different=======================
                    string Data = mySerial.ReadExisting();
                    serialBuffer.Append(Data);

                    string bufferString = serialBuffer.ToString();
                    int index = -1;
                    do
                    {
                        index = bufferString.IndexOf(terminationSequence);
                        if (index > -1)
                        {
                            string message = bufferString.Substring(0, index);
                            bufferString = bufferString.Remove(0, index + terminationSequence.Length);
                        }
                    }
                    while (index > -1); 
                    serialBuffer = new StringBuilder(bufferString);
                    byte[] bytes = new byte[30];
                    if (serialBuffer.Length == 15) {
                        Console.WriteLine("data:" + serialBuffer);
                        bytes = Encoding.ASCII.GetBytes(serialBuffer.ToString());
                     }
                    //=====Only this part is different=======================

                    this.BeginInvoke((Action)(() =>
                    {
                        data = Encoding.ASCII.GetString(bytes);

                        if (!backgroundWorker1.IsBusy)
                        {
                            backgroundWorker1.RunWorkerAsync();
                        }
                    }));

                }
                catch { alert("Can't read form  " + mySerial.PortName + " port it might be opennd in another program"); }
            }
        }

问题出在表单应用程序中,字节的值始终为空,尽管当我通过将输出值写入控制台进行检查时 window 我可以看到字节的值已更新。

我很困惑为什么变量 dataReceived 值可以被 BeginInvoke 访问,而变量字节保持空值。是否有我遗漏的东西导致值未更新?

所以我昨天没能做到,但我向 OP 保证我会在与我的评论相关的答案中写一些东西。据我了解,he/she 正在尝试根据从串口接收到的数据更新表单中的内容。以下是我的做法:

首先你需要声明一个 delegate 和一个 event (这基本上是一个委托列表):

public delegate void Data_Received_EventHandler(object sender, Data_Received_EventArgs e);
public event Data_Received_EventHandler Data_Received;

这些用于从串口替换原来的"data received"事件事件参数

为了简单起见,我通常将 Data_Received_EventArgs 类型定义为基于字节数组的类型:

public class Data_Received_EventArgs : EventArgs
{
    public byte[] data;

    internal Data_Received_EventArgs(int length)
    {
        data = new byte[length];
    }
}

那么在原来的数据接收事件中(假设你的串口叫serialPort1):

private void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{
    // check how many bytes you need to read:
    int bytesToRead = serialPort1.BytesToRead;

    // declare your arguments for the event based on that number of bytes (check the Data_Received_EventArgs constructor):
    Data_Received_EventArgs args = new Data_Received_EventArgs(bytesToRead);

    // copy the bytes from your serial port into the arguments:
    for (int i = 0; i < bytesToRead; i++)
        args.data[i] = (byte)serialPort1.ReadByte();

    // verify if there are subscribers to the event (list not empty) and fire your event using BeginInvoke:
    if (Data_Received != null)
        BeginInvoke((MethodInvoker)delegate () { Data_Received(this, args); });
}

我们现在拥有的是保证在 UI 线程上执行其 处理程序 的事件。 要使用它,您可以像这样订阅事件:

Data_Received += My_Data_Received_Handler;

你的 handler 应该与我们在第一行代码中声明的 delegate 具有相同的签名(应该是 void 并指定了参数):

private void My_Data_Received_Handler(object sender, Data_Received_EventArgs e)
{
    // read bytes from the arguments as you normally would from an array and do whatever e.g.:
    some_Label.Text = e.data[0].ToString(); // without worrying about crossthreading
}

我知道这不是 OP 想要的答案,但我希望它有助于简化 he/she 最初尝试做的事情。