'C# dictionary Value is changing unintentionally, just when I thought I knew how dictionary worked
I created a dictionary in C# in an attempt to store the latest value from a serial device. The serial device continuously sends strings of values and each string contains an ID. There are only about 7 IDs and they repeat. The dictionary is meant to capture the current string of values and store based on the ID so the latest values can be retrieved by ID. I am only interested in the latest values. A timer tic (10mS) keeps the serial buffer empty and the data is processed by other methods randomly (once > 1 sec).
The issue I am having is the dictionary value of the key,value pair is a struct:
public struct Frame
{
public bool echoMsg;
public uint pgnID;
public byte can_dlc;
public byte[] data;
public uint timestamp_us;
}
Thanks in advance for any helping me understand this issue.
All the values above are being saved with the dictPGN.Add() and if I break during runtime and inspect the dictionary I can see that everything is correct. However during runtime several more messages come in and processed and when the dictionary is read at a later time the byte[] data valves have been overwritten by strings with different IDs. I am guessing it is the way I am declaring the byte[] array as the other values in the dictionary remain valid. Ii've tried several things and searched with Google but have not found an answer.
My code looks something like this:
class SerialHardware
{
public SerialHardware(int hardwareIndexParm)
{
hardwareIndex = hardwareIndexParm;
mySerialFrame = new SerialFrame();
mySerialFrame.data = new byte[8];
}
private static UsbSerialThing MySerialControl = new UsbSerialThing();
private static int hardwareIndex;
private static SerialDeviceThing canID = new SerialDeviceTHing();
private static UsbSerialThing.frame frameBuffer = new UsbSerialThing.frame();
private static SerialFrame mySerialFrame = new SerialFrame();
private static Dictionary <UInt32, SerialFrame> dictPGN = new Dictionary<UInt32, SerialFrame>(); //saves entire frame by PGN
public struct Frame
{
public bool echoMsg;
public uint pgnID;
public byte can_dlc;
public byte[] data;
public uint timestamp_us;
}
public SerialFrame GetPNGFromBuffer( UInt32 pgnIDValue)
{
SerialFrame returnFrame;// = new SerialFrame(); <-Just some of the things I've tried
//returnFrame.data = new byte[8];
if (dictPGN.ContainsKey(pgnIDValue))
{
//returnFrame.data appears to have been updated by other buffer reads**
returnFrame = dictPGN[pgnIDValue];
return returnFrame;
}
else
{
return new SerialFrame(); //return blank frame
}
}
private static bool ReadCANDevice()
{
bool result = false;
int bufferSize = System.Runtime.InteropServices.Marshal.SizeOf(frameBuffer);
byte[] buffer = new byte[bufferSize];
int readCount = 0; //return var how many bytes in buffer
int mSTimeout = 100; //time in mSec before timeout
SerialFrame localSerialFrame = new SerialFrame();
localSerialFrame.data = new byte[8];
//Read the device
result = MySerialControl.DeviceBuf(canID, buffer, bufferSize, readCount, mSTimeout);
if (result)
{
mySerialFrame.pgnID = 0x7fffffff & (BitConverter.ToUInt32(buffer, 4));
mySerialFrame.can_dlc = buffer[8];
mySerialFrame.data[0] = buffer[12];
mySerialFrame.data[1] = buffer[13];
mySerialFrame.data[2] = buffer[14];
mySerialFrame.data[3] = buffer[15];
mySerialFrame.data[4] = buffer[16];
mySerialFrame.data[5] = buffer[17];
mySerialFrame.data[6] = buffer[18];
mySerialFrame.data[7] = buffer[19];
mySerialFrame.timestamp_us = BitConverter.ToUInt32(buffer, 20); //(uint)buffer[20] | ((uint)buffer[21] << 8) | ((uint)buffer[22] << 16) | ((uint)buffer[23] << 24);
//save message by PGN_ID
if (dictPGN.ContainsKey(mySerialFrame.pgnID))
{
//dictPGN[mySerialFrame.pgnID] = mySerialFrame;
}
else
{
//byte buffer looks good here! When I break during runtime
dictPGN.Add(mySerialFrame.pgnID, mySerialFrame);
}
}
else
{
//nothing to read, buffer empty
initDevice();
}
return result;
}
//timer event triggers read from serial device
private static void OnTimedEvent(object source, System.Timers.ElapsedEventArgs e)
{
while (ReadCANDevice())
{
//empty the buffer
}
}
}
Solution 1:[1]
In your code, you are creating a static variable mySerialFrame and setting the reference field data to point to an allocated array in the constructor.
In the ReadCANDevice method you are re-using that buffer every time you process a message. When you store the struct in the Dictionary, the fields are copied since a struct is a value type, but the value of the data field is a pointer to the allocated array from the constructor. So all the entries in the Dictionary share the same single array you overwrite each time.
Remove all instances of mySerialFrame from the class and only using localSerialFrame should fix the issue, since you allocate a new array each time you process a message for the data field of localSerialFrame.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | NetMage |
