【发布时间】:2017-02-09 10:37:41
【问题描述】:
我正在寻找 C# 中的半通用数据结构来存储不同整数和浮点类型的数组。在某些情况下,整数是位字段,其中每个位都同样重要,并且不能容忍精度损失。由于 C# 类型系统和我缺乏 C# 流畅性,我发现这很困难和混乱。
项目:Ethercat 周期性数据包到达并转换为结构(数据包)并在实验过程中累积为Packet[]。来自Packet[] 的Packet 的每个字段都被转换为一个数组。
我相信我正在寻找一种将这些数组“包装”成单一类型的方法,以便它们可以成为集合的一部分。包装它们还有其他一些优点(命名、硬件到 SI 比例因子等),以方便将硬件与后面的实现分离。
我最好的“包装器”被称为“DataWrapper”(下面简化了),但我在存储、精度损失、对象使用和代码数量方面做出了令人不安的妥协。
C# 中是否有“更好”的方法?我的黄金标准是在 Python 中使用列表或 numpy.arrays 的明显微不足道的实现,没有明显的妥协。
可以使用“对象”吗?如何?是否可以将整个数组装箱或必须将每个数组元素单独装箱(效率低下)?
我见过A list of multiple data types? 但是,对于本质上是列表的内容,似乎有很多代码和高级编程技术。
public class DataWrapper
{
private double[] double_array; // backing with double, but it could if I don't use float
private string name;
private double scale_factor_to_SI;
public DataWrapper(string name, double scale_factor, dynamic dynamic_array)
{
this.name = name;
this.scale_factor_to_SI = scale_factor;
this.double_array = new double[dynamic_array.Length];
for (int cnt = 0; cnt < dynamic_array.Length; cnt++)
{
this.double_array[cnt] = (double)dynamic_array[cnt];
}
}
public void Get(out int[] i_array)
{
i_array = this.double_array.Select(item => (int)item).ToArray();
}
public void Get(out long[] i_array)
{
i_array = this.double_array.Select(item => (long)item).ToArray();
}
public double[] GetSI()
{
return this.double_array.Select(item => this.scale_factor_to_SI * (double)item).ToArray();
}
}
public struct Packet // this is an example packet - the actual packet is much larger and will change over time. I wish to make the change in 1 place not many.
{
public long time_uS;
public Int16 velocity;
public UInt32 status_word;
};
public class example
{
public Packet[] GetArrayofPacketFromHardware()
{
return null;
}
public example() {
Packet[] array_of_packet = GetArrayofPacketFromHardware();
var time_uS = array_of_packet.Select(p => p.time_uS).ToArray();
var velocity = array_of_packet.Select(p => p.velocity).ToArray();
var status_bits = array_of_packet.Select(p => p.status_word).ToArray();
List<DataWrapper> collection = new List<DataWrapper> { };
collection.Add(new DataWrapper("time", 1.0e-6, time_uS));
collection.Add(new DataWrapper("velocity", 1/8192, velocity));
collection.Add(new DataWrapper("status", 1, status_bits));
}
}
【问题讨论】:
-
为什么不直接传递 Packet[] 而不是将其值投射出来并传递它们?
-
我会查看
List<T>,其中T是一个类,它有一个int、一个double 和一个float,还有一个type 字段说明使用了哪一个。我通常只有一个object和一个类型字段,但你提到了拆箱开销。这是一个你永远不会用像 C# 这样的强类型语言非常干净地解决的问题。使用它可能得到的最干净的东西是访问者模式类型的东西,用于遍历集合。然后,您至少可以避免在类型字段上进行切换。 FWIW。 -
我考虑过一个包装器,而不是为每种类型提供不同的支持——每种类型都有构造函数,等等。在某些方面更好,但是很多代码和输出不是很有用。似乎必须有更好的方法。如果每个数组有一个盒子,我可以装箱 - 而不是每个元素一个盒子。
-
我的问题中没有提到的是 Packet 只是一个例子。 Packet 有许多不同的结构 - 特别是因为 C# 程序配置硬件来生成 Packet。基本目标是在代码中一次性指定数据包的结构。在过去,我将创建一个表来定义数据包和物理特性(例如比例因子、名称等)。该描述用于对硬件进行编程,也用于对缓冲区进行解码。到目前为止,在 C# 中,我似乎必须制作描述的多个副本 - 这是出现错误的一种方式。
-
唯一的出路似乎是丢失类型信息。我现在明白为什么我的同事将数据包转换为文本字符串了!我可能只是将上面的包装器更改为转换为 long 并返回 long[] 或 double[](当请求缩放为 SI 单位时。)
标签: c# data-structures collections type-systems typed-arrays