Возможно ли преобразовать массив C # double[,,] в double[] без создания копии
Вопрос
У меня есть огромные 3D-массивы чисел в моем .NET-приложении.Мне нужно преобразовать их в одномерный массив, чтобы передать его в COM-библиотеку.Есть ли способ преобразовать массив без создания копии всех данных?
Я могу выполнить преобразование следующим образом, но тогда я использую вдвое больший объем памяти, что является проблемой в моем приложении:
double[] result = new double[input.GetLength(0) * input.GetLength(1) * input.GetLength(2)];
for (i = 0; i < input.GetLength(0); i++)
for (j = 0; j < input.GetLength(1); j++)
for (k = 0; k < input.GetLength(2); k++)
result[i * input.GetLength(1) * input.GetLength(2) + j * input.GetLength(2) + k)] = input[i,j,l];
return result;
Решение
Я не верю, что способ, которым C # хранит эти данные в памяти, сделал бы это осуществимым так же, как простое приведение в C.Почему бы для начала не использовать одномерный массив и, возможно, не создать класс для этого типа, чтобы вы могли обращаться к нему в своей программе, как если бы это был трехмерный массив?
Другие советы
К сожалению, массивы C # не гарантированно хранятся в непрерывной памяти, как в более приближенных к металлу языках, таких как C.Так что нет.Невозможно преобразовать double[,,] в double[] без поэлементного копирования.
Рассмотрите возможность абстрагирования доступа к данным с помощью Прокси - сервер (аналогично итераторам / смарт-указателям в C ++).К сожалению, синтаксис не такой чистый, как C ++, поскольку operator() недоступен для перегрузки, а operator[] содержит один аргумент, но все равно близок.
Конечно, этот дополнительный уровень абстракции добавляет сложности и самостоятельной работы, но это позволило бы вам вносить минимальные изменения в существующий код, который использует объекты double[,,], в то же время позволяя вам использовать один массив double[] как для взаимодействия, так и для ваших вычислений на C #.
class Matrix3
{
// referece-to-element object
public struct Matrix3Elem{
private Matrix3Impl impl;
private uint dim0, dim1, dim2;
// other constructors
Matrix3Elem(Matrix3Impl impl_, uint dim0_, uint dim1_, uint dim2_) {
impl = impl_; dim0 = dim0_; dim1 = dim1_; dim2 = dim2_;
}
public double Value{
get { return impl.GetAt(dim0,dim1,dim2); }
set { impl.SetAt(dim0, dim1, dim2, value); }
}
}
// implementation object
internal class Matrix3Impl
{
private double[] data;
uint dsize0, dsize1, dsize2; // dimension sizes
// .. Resize()
public double GetAt(uint dim0, uint dim1, uint dim2) {
// .. check bounds
return data[ (dim2 * dsize1 + dim1) * dsize0 + dim0 ];
}
public void SetAt(uint dim0, uint dim1, uint dim2, double value) {
// .. check bounds
data[ (dim2 * dsize1 + dim1) * dsize0 + dim0 ] = value;
}
}
private Matrix3Impl impl;
public Matrix3Elem Elem(uint dim0, uint dim1, uint dim2){
return new Matrix2Elem(dim0, dim1, dim2);
}
// .. Resize
// .. GetLength0(), GetLength1(), GetLength1()
}
И затем использовать этот тип как для чтения, так и для записи - 'foo[1,2,3]' теперь записывается как 'foo.Elem(1,2,3).Value', как для чтения, так и для записи значений, в левой части выражений присваивания и значения.
void normalize(Matrix3 m){
double s = 0;
for (i = 0; i < input.GetLength0; i++)
for (j = 0; j < input.GetLength(1); j++)
for (k = 0; k < input.GetLength(2); k++)
{
s += m.Elem(i,j,k).Value;
}
for (i = 0; i < input.GetLength0; i++)
for (j = 0; j < input.GetLength(1); j++)
for (k = 0; k < input.GetLength(2); k++)
{
m.Elem(i,j,k).Value /= s;
}
}
Опять же, увеличиваются затраты на разработку, но происходит совместное использование данных, устраняются накладные расходы на копирование и связанные с копированием затраты на разработку.Это компромисс.
В качестве обходного пути вы могли бы создать класс, который поддерживает массив в одномерной форме (может быть, даже в форме, близкой к голому металлу, чтобы вы могли легко передать его в библиотеку COM?), А затем перегрузить operator[] в этом классе, чтобы сделать его пригодным для использования в качестве многомерного массива в вашем коде C #.
Не зная подробностей вашей библиотеки COM, я бы подумал о создании класса facade в .Net и, при необходимости, о предоставлении доступа к COM.
Ваш фасад будет иметь двойной размер [,,] и индексатор, который будет отображать от [] до [,,].
Редактировать:Я согласен с замечаниями, сделанными в комментариях, предложение Лоренса лучше.