C# 实现不同类型数组集合的更好方法

C# 实现不同类型数组集合的更好方法,c#,data-structures,collections,type-systems,typed-arrays,C#,Data Structures,Collections,Type Systems,Typed Arrays,我正在寻找一种C#中的半通用数据结构,用于存储不同整数和浮点类型的数组。在某些情况下,整数是位字段,其中每个位都同等重要,精度损失是不可容忍的。我发现这很困难,也很混乱,因为我的C型系统和我的C型语言不流利 项目:Ethercat定期数据包到达并转换为一种结构(数据包),并在实验过程中累积为Packet[]。来自Packet[]的数据包的每个字段都转换为一个数组 我相信我正在寻找一种将这些数组“包装”为单一类型的方法,以便它们可以成为集合的一部分。包装它们还具有一些其他优点(命名、硬件到SI的比

我正在寻找一种C#中的半通用数据结构,用于存储不同整数和浮点类型的数组。在某些情况下,整数是位字段,其中每个位都同等重要,精度损失是不可容忍的。我发现这很困难,也很混乱,因为我的C型系统和我的C型语言不流利

项目:Ethercat定期数据包到达并转换为一种结构(数据包),并在实验过程中累积为
Packet[]
。来自
Packet[]
的数据包的每个字段都转换为一个数组

我相信我正在寻找一种将这些数组“包装”为单一类型的方法,以便它们可以成为集合的一部分。包装它们还具有一些其他优点(命名、硬件到SI的比例因子等),以便于将硬件与以后的实现分离

我最好的“包装器”叫做“DataWrapper”(简化如下),但使用它,我在存储、精度损失、对象使用和代码数量方面做出了令人不安的妥协

在C#中有“更好”的方法吗?我的金标准是在Python中使用list of list或numpy.array实现的简单实现,没有明显的折衷

List<dynamic> array_list = new List<dynamic> { };
public void AddArray(dynamic dynamic_array)
{
   this.array_list.Add(dynamic_array);
}
可以使用“object”吗?怎么用?是否可以装箱整个数组,或者每个数组元素必须单独装箱(效率低下)

然而,我看到了很多代码和高级编程技术,本质上是一个列表列表

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));  
    }
}
公共类数据包装器
{
private double[]double_array;//使用double进行备份,但如果不使用float,则可以
私有字符串名称;
私人双刻度系数;
公共数据包装器(字符串名称、双比例系数、动态数组)
{
this.name=名称;
这个。比例系数=比例系数;
this.double_array=新的double[dynamic_array.Length];
对于(int cnt=0;cnt(int)item.ToArray();
}
public void Get(out long[]i_数组)
{
i_array=this.double_array.Select(item=>(long)item.ToArray();
}
公共双[]GetSI()
{
返回此.double_数组。选择(item=>this.scale_factor_to_SI*(double)item).ToArray();
}
}
public struct Packet//这是一个示例数据包-实际数据包要大得多,并且会随时间而变化。我希望在一个地方做出改变,而不是在很多地方。
{
公众长期关注我们;
公共交通速度;
公共UInt32状态字;
};
公开课范例
{
公共数据包[]GetArrayofPacketFromHardware()
{
返回null;
}
公共示例(){
Packet[]数组_of_Packet=GetArrayofPacketFromHardware();
var-time\u-uS=数据包的数组。选择(p=>p.time\u-uS.ToArray();
var-velocity=数据包的数组。选择(p=>p.velocity).ToArray();
var status_bits=_数据包的数组。选择(p=>p.status_字)。ToArray();
列表集合=新列表{};
添加(新的数据包装(“时间”,1.0e-6,time_-uS));
添加(新的数据包装(“velocity”,1/8192,velocity));
添加(新的数据包装(“状态”,1,状态_位));
}
}

您可以将其视为字节[]列表,并使用位转换器序列化值类型,将值类型转换为字节[],然后使用反向调用将其展开

List<byte[]> dataList = new List<byte[]>();
float v = 1.0424f;
byte[] converted = BitConverter.GetBytes(v);
// put converted into a List<byte[]> 
dataList.Add(converted);
// Convert it back again
float z= BitConverter.ToSingle(dataList[0], 0);
List dataList=newlist();
浮球v=1.0424f;
byte[]converted=位转换器.GetBytes(v);
//把转换成一个列表
dataList.Add(已转换);
//再把它换回来
float z=BitConverter.ToSingle(数据列表[0],0);

当C#中需要持续的精度时,浮动将是有问题的。。。时期这是不幸的,因为我们都知道我们多么喜欢精确。但我不认为C#是唯一患有这种疾病的语言。也就是说,我认为有一种方法可以实现你想要的,你的包装是一个好的开始

我不熟悉您正在使用的(第三方库),所以我将坚持提供问题的解决方案

如果您知道要检索的类型,我建议使用
byte[]
。通过这种方式,您可以在单个列表中有效地存储3个
字节[]

var dataList = new List<byte[]>();
dataList.Add(ConvertUsTime(p.time_US));
dataList.Add(ConvertVelocity(p.velocity));
dataList.Add(ConvertStatus(p.status));

byte[] ConvertToArray(long usTime) {
    return BitConverter.GetBytes(usTime);
}

byte[] ConvertVelocity(Int16 velocity) {
    return BitConverter.GetBytes(velocity);
}

byte[] ConvertStatus(UInt32 status) {
    return BitConverter.GetBytes(status);
}
var dataList=newlist();
添加(ConvertUsTime(p.time_US));
添加(ConvertVelocity(p.velocity));
添加(ConvertStatus(p.status));
字节[]ConvertToArray(长usTime){
返回BitConverter.GetBytes(usTime);
}
字节[]转换速度(Int16速度){
返回BitConverter.GetBytes(速度);
}
字节[]转换状态(UInt32状态){
返回BitConverter.GetBytes(状态);
}
。。。对于更通用的方法:

byte[] ConvertValue<T>(T value) where T : struct {
    // we have to test for type of T and can use the TypeCode for switch statement
    var typeCode = Type.GetTypeCode(typeof(T));

    switch(typeCode) {
        case TypeCode.Int64:
            return BitConverter.GetBytes((long)value);

        case TypeCode.Int16:
            return BitConverter.GetBytes((Int16)value);

        case TypeCode.UInt32:
            return BitConverter.GetBytes((UInt32)value);
    }

    return null;
}
byte[]ConvertValue(T值),其中T:struct{
//我们必须测试T的类型,并且可以使用转换语句的类型代码
var typeCode=Type.GetTypeCode(typeof(T));
开关(类型代码){
case TypeCode.Int64:
返回BitConverter.GetBytes((长)值);
case TypeCode.Int16:
返回BitConverter.GetBytes((Int16)值);
案例类型代码.UInt32:
返回BitConverter.GetBytes((UInt32)值);
}
返回null;
}

您能简单地将数据序列化为JSON或MessagePack并将其存储为字符串数组吗?似乎这将是相对简单的实现和易于使用。

作为通用列表方法的反例,我想提到的是,问题中链接的列表示例不应被视为高级。它使用了一个简单的C#接口

当您希望调试列表的内容或希望调试列表上的业务逻辑时,使用实现相同接口的不同类型可能是更好的解决方案
List<dynamic> array_list = new List<dynamic> { };
public void AddArray(dynamic dynamic_array)
{
   this.array_list.Add(dynamic_array);
}
int ndx = 0;
foreach (var array_from_list in this.array_list) {                
  var v = array_from_list[ndx];  // error if array_from_list is ArrayList
}
using System;
using System.Collections.Generic;


namespace Application
{
    class MyTest
    {
        List<dynamic> array_list = new List<dynamic> { };
        int length;

        public void AddArray(dynamic dynamic_array)
        {
            this.array_list.Add(dynamic_array);
            this.length = dynamic_array.Length;
        }
        public dynamic GetVector(int ndx)
        {
            return array_list[ndx];
        }
        public void Display()
        {
            for (int ndx = 0; ndx < this.length; ndx++)
            {
                string ln_txt = "";
                foreach (var array_from_list in this.array_list)
                {
                    string s = array_from_list[ndx].ToString();
                    ln_txt += $"{s} ";
                }

                Console.WriteLine(ln_txt);
            }

        }
    }

    static class Program
    {


        [STAThread]
        static void Main(string[] args)
        {

            MyTest test = new MyTest();
            test.AddArray(new long[] { 10, 20, 30, 40 });
            test.AddArray(new int[] { 1, 2, 3, 4 });
            test.AddArray(new double[] { .1, .2, .3, .4 });
            test.AddArray(new string[] { "a", "b", "c", "d" });
            test.Display();


            for (int vecnum = 0; vecnum < 4; vecnum++)
            {
                var vector = test.GetVector(vecnum);
                Console.Write($"vnum:{vecnum} :   ");

                foreach (var value in vector)
                {
                    Console.Write($"{value}   ");
                }
                Console.WriteLine("");
            }

        }
    }
}