C# позволяет создавать массив без его инициализации с помощью GC.AllocateUnitializedArray<T>
. T
может быть любого типа, например bool
или int
. Однако я не знаю, как сделать это с 2D-массивом, например. bool[,]
или int[,]
.
Я знаю, что могу выделить 1D-массив размером с 2D-массив, а затем получить к нему доступ, как к 2D-массиву, например:
int[] array = GC.AllocateUninitializedArray<int>(width * height); //width and height are width and height of the array
// ... (array is initialized)
int arrayAccessExample = array[x * height + y]; //x and y are anything greater than 0 and less than width and height, respectively
Но постоянно делать это раздражает, и если все сделать неправильно, это может легко привести к ошибкам. Я мог бы превратить доступ к массиву в функцию, которая устранила бы возможность возникновения ошибок, но это все равно неудобно, и я бы предпочел просто иметь возможность использовать настоящий 2D-массив. Есть ли способ напрямую выделить неинициализированный 2D-массив или «привести» 1D-массив к 2D-массиву?
Я бы подумал о том, чтобы пойти по этому пути, чтобы облегчить вам задачу:
public class UnallocatedArray<T>
{
public UnallocatedArray(int width, int height)
{
array = GC.AllocateUninitializedArray<T>(width * height);
this.Width = width;
this.Height = height;
}
private int Width { get; init; }
private int Height { get; init; }
private T[] array = null;
public T this[int x, int y]
{
get => array[x * this.Height + y];
set => array[x * this.Height + y] = value;
}
}
Тогда вы сможете написать:
var ua = new UnallocatedArray<int>(4, 5);
ua[2, 1] = 42;
Console.WriteLine(ua[2, 1]);
Стоит обратить внимание на порядок следования строк/столбцов, поскольку это может повлиять на совместимость. Встроенный двумерный массив и ваш пример — это основной столбец. В то время как такие вещи, как изображения, почти всегда являются основными строками.
Есть ли способ напрямую выделить неинициализированный 2D-массив?
Нет, вообще невозможно. Хотя код находится внутри функции AllocateArrayEx в gchelpers.cpp, необязательный параметр flags
не предоставляется ни одной функцией FCall или QCall. Поэтому это невозможно сделать даже с помощью отражения без перекомпиляции CLR.
Вы можете и должны открыть задачу GitHub с запросом этой функции, поскольку реализовать ее довольно просто.
или «привести» 1D-массив к 2D-массиву?
Тоже нет. Это совершенно разные типы и даже пути размещения совершенно разные: SZArray (1D, с нулевым индексом) и другие массивы.
Я предполагаю, что теоретически вы могли бы выделить обычный массив, а затем сделать какую-то ужасно небезопасную манипуляцию с m_pMethTab
, чтобы заменить его другим, и просто надеяться, что все это «работает», но я бы не стал использовать это в производстве.
Но, на мой взгляд, если ваши требования к производительности достаточно высоки, чтобы требовать неинициализированные массивы, то вам, вероятно, все равно не нужны обычные 2D-массивы, поскольку они имеют значительные накладные расходы из-за отсутствия оптимизации во время выполнения для нескольких массивов. .
В библиотеке ️Span2D
Microsoft.Toolkit.HighPerformance также есть тип struct
, или вы можете создать свой собственный (вероятно, class
будет быстрее, чем flags
, как в @Enigmativity), чтобы позволить вам обрабатывать 1D-массив как 2Д. Возможно, вам все равно придется писать код осторожно, чтобы избежать множества ненужных проверок границ.
Спасибо за быстрый ответ! Вы уверены, что невозможно выполнить приведение из массива 1d в массив 2d? Если нет, ничего страшного — ваш код работает отлично. (редактировать: Чарлифейс, я не знал о существовании Span2D, спасибо!)