Преобразование массива объектов массивов объектов в двумерный массив объекта
У меня есть сторонняя библиотека, возвращающая массив объектов массивов объектов, которые я могу вставить в объект []:
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];