11

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

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

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

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:像詹姆斯一样,我觉得扩展方法有点危险,因为它适用于所有object[]变量。
@Nair:我为我的实现选择了你的答案,因为它不需要使用 Linq 包装器,我可以int i = (int) arr[row][col];使用 @quetzalcoatl 和 @Abe Heidebrecht 访问单个字段或整个行object[] row = arr[row];
:感谢关于Cast<>().

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

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

公共类 CustomArray { 私有对象 [] 数据;公共 CustomArray(object[] 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
        }

    }

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

4

5 回答 5

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保持它足够通用,以便您决定需要什么数据类型转换。

于 2013-06-26T14:24:15.990 回答
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.
            }

        }
于 2013-06-26T14:30:47.863 回答
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。但我喜欢它。我建议詹姆斯的包装。

于 2013-06-26T14:28:02.223 回答
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

于 2013-06-26T14:29:07.437 回答
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];
于 2013-06-26T14:27:17.480 回答