将对象数组的对象数组转换为对象的二维数组

时间:2013-06-26 14:16:27

标签: c# arrays linq

我有一个第三方库返回一个对象数组的对象数组,我可以把它放到一个对象中[]:

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包装,而索引器需要行和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         {             私有对象[]数据;             public CustomArray(object [] arr)             {                 data = 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
        }

    }

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

5 个答案:

答案 0 :(得分:7)

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

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使其足够通用,以便您决定需要哪种数据类型。

答案 1 :(得分:3)

几乎没有类似的答案发布,它做了类似的事情。只有当你想要像

这样的时候才会有所不同
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.
            }

        }

答案 2 :(得分:1)

(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<object[]>,不同之处在于它将被懒惰地枚举,因此对所有元素的额外迭代消失了,临时额外数组消失了,而且开销也很小 - 类似于每个元素的手动转换,无论如何都要做。但是 - 你失去了对最顶层数组进行索引的能力。您可以对其进行循环/ foreach,但不能将其编入/ [123]

编辑:

詹姆斯的包装方式在整体表现方面可能是最好的。我最喜欢它的可读性,但这是个人观点。其他人可能更喜欢LINQ。但我喜欢它。我建议詹姆斯的包装纸。

答案 3 :(得分:1)

您可以使用扩展方法:

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

答案 4 :(得分:0)

您可以使用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];