【问题标题】:c# how to convert array of struct to struct of arrayc# 如何将结构数组转换为数组结构
【发布时间】:2016-09-29 18:33:45
【问题描述】:

我从发送周期性数据包的硬件源中的结构化数据开始使用 IntPtr。线程从 Intptr 缓冲区复制数据包并将 byte[] 放入队列中。稍后读取队列并将 byte[] 转换为使用 this answer 的结构,并成为 Packet 类型的集合(数组)。

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

Packet[] array_of_packet; 

不重复结构定义,我想使用数据,就好像它是一个数组结构。

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

ArrayOfPacket data;  

这种格式允许其他函数将它们用作向量:

Plot(data.time, data.field1);

【问题讨论】:

  • 我输了!无论如何,它应该是 long[] 时间而不是 long time[]
  • 嗯,这是一个完全不同的布局。您需要逐项手动复制所有内容。
  • 你真的应该考虑创建immutable structs

标签: c# arrays data-structures struct


【解决方案1】:

如果您想要的只是转换为数组的简单方法,那么只需使用 LINQ:

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

如果您正在寻找一种神奇的方法来重新排列内存中的数据而无需复制成本,那么您就不走运了。 ;-)

【讨论】:

    【解决方案2】:

    使用 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();
    

    虽然您需要将属性设置为public

    一个更有效的解决方案可能是

    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;
    

    【讨论】:

      【解决方案3】:

      您可以创建一个封装类来公开类似数组的访问器语法:

      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]);}
          }
      
      }
      

      然后,使用这样的简单辅助方法:

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

      你可以像这样创建一个对象:

      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)
      };
      

      ...可以按照你想要的方式使用:

      Plot(data.time, data.field1);
      

      根据您的具体需求,您可以通过多种方式对此进行详细说明:

      • 使该类实现类似IList&lt;TField&gt; 的接口,以便Plot() 可以以与底层类型无关的方式编写。 (数组已经实现了IList&lt;TField&gt;。)
      • 如果性能是高优先级,而不是使用委托 (Func&lt;,&gt;),您可以将 where T is struct 添加到 ArrayWrapper 定义中,并让 ArrayWrapper 将字节偏移量作为其构造函数参数,并且做一些unsafe 魔术以更快地访问该字段的数据。

      【讨论】:

      • 当我问这个问题时——这就是我要找的——
      • @brainfog:我想可能是这样。我喜欢 LINQ,但听起来您试图避免将数据复制到多个数组中。
      • 最初我是一步一步地考虑的:1) 收集数据 2) 转换为 struct 和它们的数组 3) 转换为数组结构而不重新键入原始结构(因为我有很多,以后可能会改变)
      猜你喜欢
      • 1970-01-01
      • 2019-04-04
      • 2023-04-08
      • 2015-06-22
      • 2022-08-04
      • 2021-11-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多