Преобразование массива объектов массивов объектов в двумерный массив объекта

У меня есть сторонняя библиотека, возвращающая массив объектов массивов объектов, которые я могу вставить в объект []:

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

Результирующий массив состоит из объектов [] записей, поэтому вы можете думать о возвращаемом значении как о некотором наборе записей с внешним массивом, представляющим строки и внутренние массивы, содержащие значения полей, где некоторые поля могут не заполняться (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;, но это не выполняется с ошибкой времени выполнения.

Теперь, мои вопросы:

  • Есть ли более простой способ сделать это? У меня такое ощущение, что некоторые Отличный актер должен сделать трюк?
  • Также меня беспокоит производительность так как я должен переделать много данных, чтобы спасти меня от кастинга, поэтому я интересно, действительно ли это стоит?

EDIT: Спасибо всем за быстрые ответы. @James: Мне нравится, что ваш ответ завершает виновника в новом классе, но недостатком является то, что мне всегда приходится делать упаковку Linq при взятии в исходном массиве, а индексатору нужны значения строк и столбцов int i = (int) arr[row, col]; (мне нужно чтобы получить полный ряд, а также object[] row = arr[row];, извините, не опубликовал это в начале).
@Sergiu Mindras: Как и Джеймс, я чувствую, что метод расширения немного опасен, поскольку он применим ко всем переменным object[].
@Nair: я выбрал свой ответ для моей реализации, так как ему не нужно использовать оболочку Linq, и я могу получить доступ к обоим отдельным полям с помощью int i = (int) arr[row][col]; или всей строки с помощью object[] row = arr[row];
@quetzalcoatl и @Abe Heidebrecht: Спасибо за подсказки на Cast<>().

Заключение: Я бы хотел выбрать ответ Джеймса и Найра, но, как я уже сказал выше, решение Nair дает мне (я думаю) лучшую гибкость и производительность. Я добавил функцию, которая будет "сглаживать" внутренний массив, используя вышеуказанный оператор Linq, потому что у меня есть другие функции, которые нужно подавать с такой структурой.

Вот как я (грубо) реализовал его (взято из решения Найра:

открытый класс CustomArray       {           данные частного объекта [];           public CustomArray (объект [] 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
        }

    }

Итак - еще раз - спасибо всем! Это всегда настоящее удовольствие и просвещение, чтобы использовать этот сайт.

Ответ 1

Существует несколько похожих ответов, которые делают что-то подобное. Это отличается только в том случае, если вы хотите использовать как

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

Вы можете создать класс-оболочку, чтобы скрыть уродливое литье, например.

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, он будет достаточно общим для вас, чтобы решить, какой тип данных должен быть выбран.

Ответ 3

(1) Возможно, это может быть сделано в короткой и легкой форме с ключевым словом dynamic, но вы будете использовать проверку времени компиляции. Но учитывая, что вы используете объект [], это небольшая цена:

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] его.

EDIT:

Способ обмана Джеймса, вероятно, является лучшим с точки зрения общей производительности. Мне больше всего нравится читаемость, но это личное мнение. Другие могут понравиться LINQ. Но мне нравится это. Я бы предложил обертку Джеймса.

Ответ 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].

ИЗМЕНИТЬ

Спасибо Джеймсу за ваше наблюдение

Вы можете использовать nullable 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, и все обнаруженные исключения принудительно возвращают null

Ответ 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];