【问题标题】:Convert an object array of object arrays to a two dimensional array of object将对象数组的对象数组转换为对象的二维数组
【发布时间】:2013-06-23 17:51:58
【问题描述】:

我有一个第三方库返回一个对象数组的对象数组,我可以将其填充到一个对象[]中:

object[] arr = myLib.GetData(...);

结果数组由 object[] 条目组成,因此您可以将返回值视为某种记录集,其中外部数组表示行,而内部数组包含可能未填充某些字段的字段值(a锯齿状阵列)。要访问各个字段,我必须像这样投射:

int i = (int) ((object[])arr[row])[col];//access a field containing an int

现在我很懒,我想访问这样的元素:

int i = (int) arr[row][col];

为此,我使用以下 Linq 查询:

object[] result = myLib.GetData(...);
object[][] arr = result.Select(o => (object[])o ).ToArray();

我尝试使用像 object[][] arr = (object[][])result; 这样的简单强制转换,但失败并出现运行时错误。

现在,我的问题:

  • 有更简单的方法吗?我感觉有些 漂亮的演员应该可以解决问题吗?
  • 我也担心性能 因为我必须重塑大量数据只是为了节省一些演员,所以我 想知道这是否真的值得?

编辑: 谢谢大家的快速解答。
@James:我喜欢你的回答将罪魁祸首包裹在一个新类中,但缺点是我在接收源数组时总是需要进行 Linq 包装,并且索引器需要 row 和 col 值int i = (int) arr[row, col];(我需要像object[] row = arr[row];一样获得完整的行,抱歉,一开始没有发布)。
@Sergiu Mindras:和 James 一样,我觉得扩展方法有点危险,因为它适用于所有 object[] 变量。
@Nair:我为我的实现选择了你的答案,因为它不需要使用 Linq 包装器,我可以使用 int i = (int) arr[row][col]; 访问两个单独的字段或使用 object[] row = arr[row]; 访问整行
@quetzalcoatl 和@Abe Heidebrecht:感谢Cast<>() 的提示。

结论:我希望我可以同时选择 James 和 Nair 的答案,但正如我上面所说,Nair 的解决方案给了我(我认为)最好的灵活性和性能。 我添加了一个函数,该函数将使用上述 Linq 语句“展平”内部数组,因为我还有其他需要使用这种结构的函数。

这是我(大致)实现它的方式(取自 Nair 的解决方案:

公共类 CustomArray { 私有对象[] 数据; 公共CustomArray(对象[] arr) { 数据=arr; }

        //get a row of the data
        public object[] this[int index]
        { get { return (object[]) data[index]; } }

        //get a field from the data
        public object this[int row, int col]
        { get { return ((object[])data[row])[col]; } }

        //get the array as 'real' 2D - Array
        public object[][] Data2D()
        {//this could be cached in case it is accessed more than once
            return data.Select(o => (object[])o ).ToArray()
        }

        static void Main()
        {
            var ca = new CustomArray(new object[] { 
                      new object[] {1,2,3,4,5 },
                      new object[] {1,2,3,4 },
                      new object[] {1,2 } });
            var row = ca[1]; //gets a full row
            int i = (int) ca[2,1]; //gets a field
            int j = (int) ca[2][1]; //gets me the same field
            object[][] arr = ca.Data2D(); //gets the complete array as 2D-array
        }

    }

所以 - 再次 - 谢谢大家!使用这个网站总是一种真正的乐趣和启发。

【问题讨论】:

  • 什么是运行时错误?
  • 这里最昂贵的操作是从objectint(和其他类型)的拆箱,这似乎是不可避免的,因为你的lib 只返回object[]。你确定它不提供类型化接口吗?
  • 什么是 var[] arr = myLib.GetData(...);在这种情况下给你?
  • @Andre:返回的数据由不同的类型组成,而且,不,没有类型化接口,因为该函数基本上返回一个select语句的结果,该语句可以包含许多不同类型的字段。

标签: c# arrays linq


【解决方案1】:

您可以创建一个包装类来隐藏丑陋的铸造,例如

public class DataWrapper
{
    private readonly object[][] data;

    public DataWrapper(object[] data)
    {
        this.data = data.Select(o => (object[])o ).ToArray();
    }

    public object this[int row, int col]
    {
        get { return this.data[row][col]; }
    }
}

用法

var data = new DataWrapper(myLib.GetData(...));
int i = (int)data[row, col];

还有机会使包装器通用,例如DataWrapper<int>,但是,我不确定您的数据集合是否都是同一类型,返回 object 使其足够通用,以便您决定需要什么数据类型转换。

【讨论】:

  • 一个想法:使用您当前的解决方案,每次用户调用data[1, 1],都会计算一次拆箱。那么,为什么不使用提供的代码 OP 将 object[] 转换为 object[][]
  • 我会争论那个拆箱。如果项目被多次阅读,它实际上会加快整体使用速度。但是,如果这组项目只被读取一次并立即处理,则预拆箱将影响性能,可能会导致更高的内存使用量而没有真正的收益。考虑从数据库中获取数据时动态生成的数据流。迭代并缓存数百万个object[],只是为了不将它们拆箱两次..?这是一个应该严格针对具体用例量身定制的优化。请不要建议“仅仅因为它更好”。
  • @Quetzalcoatl 这是一个公平的观点,但是,假设 OP 正在 将读取所有信息,那么这可能是正确的方法。让我更新解决方案,使其在两种情况下都很灵活......
  • 我的意思是读取一次(不那么值得努力)与多次读取(值得努力 N 次)。我注意到顶层数据对象是object[],所以所有数据都已经在内存中,但是缓存有效地使顶层数组的内存加倍。这是我想添加的唯一警告!正如我已经写过的,我喜欢这个解决方案。
  • @Quetzalcoatl "但是,如果这组项目只被读取一次并立即处理,那么预拆箱会影响性能" - 如果 all 至少在预先拆箱后才能读取这些项目会更好吗?否则你会拆箱 per index.
【解决方案2】:

几乎没有类似的答案发布了类似的东西。仅当您想像

一样访问时,这才有所不同
int i = (int) arr[row][col]; 

展示想法

   public class CustomArray
        {
            private object[] _arr;
            public CustomArray(object[] arr)
            {
                _arr = arr;
            }

            public object[] this[int index]
            {
                get
                {
                    // This indexer is very simple, and just returns or sets 
                    // the corresponding element from the internal array. 
                    return (object[]) _arr[index];
                }
            }
            static void Main()
            {
                var c = new CustomArray(new object[] { new object[] {1,2,3,4,5 }, new object[] {1,2,3,4 }, new object[] {1,2 } });
                var a =(int) c[1][2]; //here a will be 4 as you asked.
            }

        }

【讨论】:

    【解决方案3】:

    (1) 这可能可以使用dynamic 关键字以简明扼要的形式完成,但您将使用编译时检查。但是考虑到你使用 object[],这是一个不小的代价:

    dynamic results = obj.GetData();
    object something = results[0][1];
    

    不过我没有用编译器检查过。

    (2) 代替Select(o => (type)o) 有一个专用的Cast<> 函数:

    var tmp = items.Select(o => (object[])o).ToArray();
    var tmp = items.Cast<object[]>().ToArray();
    

    它们几乎相同。我猜 Cast 会快一点,但我也没有检查过。

    (3) 是的,以这种方式重塑会在一定程度上影响性能,主要取决于项目的数量。您拥有的元素越多,影响就越大。这主要与 .ToArray 相关,因为它将枚举所有项目并创建一个额外的数组。考虑一下:

    var results = ((object[])obj.GetData()).Cast<object[]>();
    

    这里的'结果'是IEnumerable&lt;object[]&gt;类型,不同的是它会被延迟枚举,所以对所有元素的额外迭代没有了,临时的额外数组也没有了,而且开销也很小——类似手动转换每个元素,无论如何你都会这样做..但是 - 你失去了索引最顶层数组的能力。你可以在它上面循环/foreach,但是你不能索引/[123]它。

    编辑:

    就整体性能而言,James 的包装方式可能是最好的。我最喜欢它的可读性,但这是个人意见。其他人可能更喜欢 LINQ。但我喜欢它。我建议 James 的包装。

    【讨论】:

      【解决方案4】:

      你可以使用扩展方法:

      static int getValue(this object[] arr, int col, int row)
      {
          return (int) ((object[])arr[row])[col];
      }
      

      并通过

      检索
      int requestedValue = arr.getValue(col, row);
      

      不知道 arr[int x][int y] 语法。

      编辑

      感谢詹姆斯的观察

      您可以使用可为空的 int,这样在转换时就不会出现异常。

      所以,方法会变成:

      static int? getIntValue(this object[] arr, int col, int row)
      {
          try
          {
          int? returnVal = ((object[])arr[row])[col] as int;
          return returnVal;
          }
          catch(){ return null; }
      }
      

      并且可以通过

      检索
      int? requestedValue = arr.getIntValue(col, row);
      

      这样你得到一个可以为空的对象并且所有遇到的异常都强制返回null

      【讨论】:

      • 我认为这是对扩展方法的滥用。它应该足够通用以用于object[] 的任何实例,在这种情况下,您假设所有object[] 都将包含一个内部object[] 并且是int 类型。
      【解决方案5】:

      您可以使用 LINQ Cast 运算符代替 Select...

      object[][] arr = result.Cast<object[]>().ToArray()
      

      这有点不那么冗长,但在性能方面应该几乎相同。另一种方法是手动完成:

      object[][] arr = new object[result.Length][];
      for (int i = 0; i < arr.Length; ++i)
          arr[i] = (object[])result[i];
      

      【讨论】:

        猜你喜欢
        • 2020-11-10
        • 2015-10-22
        • 2018-11-24
        • 1970-01-01
        • 1970-01-01
        • 2020-11-19
        • 1970-01-01
        • 2019-08-06
        • 1970-01-01
        相关资源
        最近更新 更多