【问题标题】:Indexer created to look at array doesn't see changes made to array为查看数组而创建的索引器看不到对数组所做的更改
【发布时间】:2015-10-12 10:36:18
【问题描述】:

我对 c# 很陌生,我正在为我设计的板创建一个串行端口类。其中此类包含打开/关闭连接到板的串行端口的方法。它还应该从板读取消息并将消息从 UI 写入板(我正在使用表单应用程序来输入和显示值)。

我读取内部输入缓冲区并将字节放入我自己的软件缓冲区中,当消息完成时,这将提示表单分析消息...

为此,我创建了一个索引器来指向数组(来自表单)并获取它想要的字节。

    uint[] serialPortReceiveBuffer = new uint[3];
    public delegate void Del();
    Del promptFormAction = Form1.MsgReceived;

    public void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
    {   
        for (int i = 0; i <= 2; i++) 
        {
            serialPortReceiveBuffer[i] = (uint)serialPort1.ReadByte();
        }

        promptFormAction();  
    }

    public uint this[uint i]
    {
        get { return serialPortReceiveBuffer[i]; }
    }

这是我的pcbSerialPort类中的代码,Form1类中与之相关的代码如下:

    public static void MsgReceived()
    {
        Form1 _frm = new Form1();
        _frm.analyzeIncomingMessage();
    }

    public void analyzeIncomingMessage()
    {
        if (PCB[0] == 63)
        {
            setBoardDesignator(PCB[1], PCB[2]);
        }
    }

我的问题是,当我使用索引器访问serialPortReceiveBuffer 时,它看不到我在将接收到的字节放入同一个数组时对其所做的更改。例如,当我收到我自己的协议字符串时 --> "?10" 缓冲区被 [63][49][48]

虽然当我尝试使用索引器访问此缓冲区时,我得到 [0][0][0]

请问有人可以帮忙吗?此外,我知道可能还有其他一些事情我可以做得更好,所以如果你有任何一般性的提示会很棒。也是一种我可以理解的语言。我只是了解 c# 的许多方面,过去一年我一直在做嵌入式软件,但我不认为自己是一个称职的程序员。

谢谢

【问题讨论】:

  • PCB 在哪里定义?你确定你在看同一个实例吗?你确定DataRecieved 事件甚至被触发了吗? IIRC,在触发该事件之前必须满足一个阈值(字节数)。
  • 您好,感谢您的回复,是的,DataReceived 事件肯定是在调试模式下触发的,我可以看到我的 PCB 已成功发送消息“?10”,并且正确的值已存储在大批。我在 promptFormAction() 旁边放了一个标记;所以我会以完整的形式看到数组。然后在索引器中也放一个标记并按下播放键,只看到数组是空的。
  • PCB 在顶部的 Form1 类中定义,如下所示:pcbSerialPort PCB = new pcbSerialPort();
  • @JonathanBrown 你有它。通过在 MsgReceived 方法中创建一个新实例,您可以使用字节数组的一个普通的新实例。您应该像我的回答一样研究如何正确使用事件,这将使您摆脱这个问题。
  • @ThorstenDittmar 答案很好,您应该阅读它。至少,如果您不想接受 Thorsten 的建议并重新设计它,则需要将收到消息的 pcbSerialPort 的实例传递给您的静态 MsgReceieved 函数,然后将其传递给 @987654329 的构造函数@ 所以它可以存储引用而不是创建一个新的(和单独的)引用。

标签: c# arrays properties indexer


【解决方案1】:

根据您的代码,我不太确定您在表单中使用的 PCB 对象实际上是接收数据的对象。很可能您正在使用两个不同的实例,尤其是当您在数据进入时创建 Form1 的新实例时!

(编辑:从您对问题的评论很明显,这正是问题所在。按照这些说明来接近您想要的)。

我建议您重新设计代码,将接收到的消息作为事件传递给现有的表单实例,而不是现在的方式。您可能会遇到的另一个问题是,您认为自己获得的数据将被下一条消息覆盖,因为DataReceived 事件是异步的。

我会声明一个表单实例可以订阅的事件,将要分析的数据传递给事件:

 public class MessageReceivedEventArgs: EventArgs
 {
     public MessageReceivedEventArgs(byte[] data) : base()
     {
         Data = data;
     }

     public byte[] Data
     {
         get;
         private set;
     }
}

public event EventHandler<MessageReceivedEventArgs> MessageReceived;

然后,我将您的DataReceivedevent 更改如下:

public void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{   
    for (int i = 0; i <= 2; i++) 
    {
        serialPortReceiveBuffer[i] = (uint)serialPort1.ReadByte();
    }

    byte[] dataCopy = new byte[serialPortReceiveBuffer.Length];
    Array.Copy(serialPortReceiveBuffer, dataCopy, dataCopy.Length);

    promptFormAction(dataCopy);  
}

private void promptForAction(byte[] data)
{
    if (MessageReceived != null)
        MessageReceived(this, new MessageReceivedEventArgs(data));
}

另外,我会将serialPortReceiveBuffer 完全保留给该类,正如我所说,如果不这样做,您可能会遇到同步问题。这就是我在将数组传递给事件之前复制数组的原因。

此更改允许任何订阅者在您意识到有新数据进入时注册通知。

要使用它,Form1 应该看起来像这样(大致);

public class Form1
{
    pcbSerialPort PCB; // The name of that class I don't know from your code

    public Form1()
    {
        PCB = new pcbSerialPort();
        PCB.MessageReceived += MessageReceived;
    } 

    private void MessageReceived(object sender, pcbSerialPort.MessageReceivedEventArgs e)
    {
        analyzeIncomingMessage(e.Data);
    }

    private void analyzeIncomingMessage(byte[] data)
    {
        if (data[0] == 63)
        {
            setBoardDesignator(data[1], data[2]);
        }
    }
}

关于如何处理串行数据的另一条建议:您需要决定是循环读取串行端口还是依赖DataReceived 事件。将循环放入事件中不是一个好主意,因为在您等待时,可能会在到达数据时再次调用该事件。

您需要做的是创建一个缓冲区,从可用的串行端口获取所有信息。如果您没有足够的数据,请不要等待。而是在调用 DataReceived 时添加到缓冲区,并在存在足够数据时处理消息。

【讨论】:

  • 感谢@ThorstenDittmar 试图理解它,首先我将尝试在我的代码中实现它。我遇到了“byte[] dataCopy = serialPortReceiveBuffer.Copy();”的问题声明它说复制方法没有重载
  • 啊,好的。我会采取不同的做法,稍后更新我的答案。
  • 错误 1 ​​可访问性不一致:参数类型“WindowsFormsApplication1.pcbSerialPort.MessageReceivedEventArgs”比方法“WindowsFormsApplication1.Form1.MessageReceived(object, WindowsFormsApplication1.pcbSerialPort.MessageReceivedEventArgs)”更难访问 D:\Users\jbrown\ Documents\Home work visual\New HMI Design\HMI_071-02\Form1.cs 153 21 HMI_071-02
  • 我收到关于 Form1 类中收到的消息方法的错误
  • 我猜pcbSerialPort 类是不公开的。您可以安全地将MessageReceived 和analyzeIncomingMessage 的访问修饰符更改为private,就像我将在我的代码中所做的那样。您收到此错误的原因是您无法将可访问性受限的内容传递给具有公共可访问性的内容。
【解决方案2】:

我认为 Thorsten 的回答很好,按照这些思路重新设计它会很有意义,但作为绝对最低限度,如果您希望为每条收到的消息创建一个新的 Form1 实例,那么您需要将pcbSerialPort 的实例传递给MessageReceived,然后再传递给Form1 类的构造函数。比如:

Action<pcbSerialPort> promptFormAction = Form1.MsgReceived;

public void serialPort1_DataReceived(object sender, SerialDataReceivedEventArgs e)
{   
    // as Thorsten noted, you need to rethink this loop anyway
    // what if there aren't at least three bytes to read?
    for (int i = 0; i <= 2; i++) 
    {
        serialPortReceiveBuffer[i] = (uint)serialPort1.ReadByte();
    }

    promptFormAction(this);  
}

还有你的静态方法:

public static void MsgReceived(pcbSerialPort pcb)
{
    Form1 _frm = new Form1(pcb);
    _frm.analyzeIncomingMessage();
}

你是Form1的构造函数:

public Form1(pcbSerialPort pcb)
{
    PCB = pcb;
}

【讨论】:

  • 我想我们同意这样一个事实,即有很多理由不按照他的方式去做,其中之一是实际的 UI 独立类突然与UI 作为硬编码 Form1.MsgReceived 方法的需要。
  • @ThorstenDittmar:我假设(也许)OP 的想法是能够注入Action 来处理接收到的数据。当然,这里和 OP 的原始代码都对其进行了硬编码,但您可以将 promptFormAction 设为属性,或使用构造函数参数设置它,然后删除与 UI 的硬编码耦合。
  • 我不想在每次收到消息时都创建一个新的 Form1 实例。我只是这样做,因为我不知道任何其他方式。我正在学习,但它阅读的内容与实际实施它们不同。可能会给我一个例子,当我尝试它时它不一定有效
  • @ThorstenDittmar:感谢您的帮助,它现在可以正常工作,我了解您现在所做的一切,这绝对是有道理的!问候
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多