简体   繁体   中英

c# how to convert array of struct to struct of array

I'm starting with an IntPtr to structured data from a hardware source that sends periodic packets. A thread copies a packet from the Intptr buffer and places byte[] into a queue. Later the queue is read and byte[] is converted to a structure using this answer and becomes a collection (array) of type Packet.

struct Packet {
  long time;
  int field1;
  short field2;
}

Packet[] array_of_packet; 

Without duplicating the structure definition, I would like to use the data as if it was a structure of arrays.

struct ArrayOfPacket {
  long[] time;
  int[] field1;
  short[] field2;
}

ArrayOfPacket data;  

This format allows other functions use them as vectors:

Plot(data.time, data.field1);

If all you want is an easy way to transform to arrays, then just use LINQ:

Plot (array_of_packet.Select(p => p.time).ToArray(),
      array_of_packet.Select(p => p.field1).ToArray());

If you're looking for a magic way to rearrange the data in memory without the cost of copying, then you're out of luck. ;-)

Pretty easy with Linq

data.time = array_of_packet.Select(p => p.time).ToArray();
data.field1 = array_of_packet.Select(p => p.field1).ToArray();
data.field2 = array_of_packet.Select(p => p.field2).ToArray();

Though you'll need to make the properties public

A more effecient solution might be

var time = new long[array_of_packet.Length];
var field1 = new int[array_of_packet.Length];
var field2 = new short[array_of_packet.Length];
for(int i = 0; i < array_of_packet.Length; i++)
{
    time[i] = array_of_paket[i].time;
    field1[i] = array_of_paket[i].field1;
    field2[i] = array_of_paket[i].field2;
}

data.time = time;
data.field1 = field1;
data.field2 = field2;

You could create a wrapper class that exposes an array-like accessor syntax:

class ArrayWrapper<T, TField>
{
    Func<T, TField> getField;
    T[] array;

    public ArrayWrapper(T[] array, Func<T, TField> getField)
    {
        this.array = array;
        this.getField = getField;
    }

    public TField this[int index]
    {
        get { return this.getField(this.array[index]);}
    }

}

Then, with a simple helper method like this:

ArrayWrapper<T, TField> Wrap<T, TField>(T[] array, Func<T, TField> getField)
{
    return new ArrayWrapper<T, TField>(array, getField);
}

You could create an object like this:

var data = new
{
    time = Wrap(array_of_packet, p => p.time),
    field1 = Wrap(array_of_packet, p => p.field1),
    field2 = Wrap(array_of_packet, p => p.field2)
};

... which could be used the way you want:

Plot(data.time, data.field1);

Depending on your specific needs, you could elaborate on this in a number of ways:

  • Make the class implement an interface like IList<TField> so that Plot() can be written in a way that's agnostic of the underlying type. (Arrays already implement IList<TField> .)
  • If performance is a high priority, rather than using a delegate ( Func<,> ), you could add where T is struct to the ArrayWrapper definition, and have the ArrayWrapper take a byte offset as its constructor parameter, and do some unsafe magic to access the field's data more quickly.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM