Я занят переписыванием старого проекта, который был сделан на C++, на C#.
Моя задача - переписать программу так, чтобы она функционировала как можно ближе к оригиналу.
Во время обработки файлов предыдущий разработчик, написавший эту программу, создает структуру, содержащую тонну полей, соответствующих заданному формату, в котором должен быть записан файл, так что вся эта работа уже сделана за меня.
Все эти поля представляют собой байтовые массивы. Затем код на C++ использует memset
для установки всей этой структуры на все символы пробелов (0x20
). Одна строка кода. Легко.
Это очень важно, поскольку утилита, в которую в конечном итоге попадает этот файл, ожидает файл в этом формате. Мне пришлось изменить эту структуру на класс в C#, но я не могу найти способ легко инициализировать каждый из этих байтовых массивов всеми символами пробела.
В итоге мне пришлось сделать следующее в конструкторе класса:
//Initialize all of the variables to spaces.
int index = 0;
foreach (byte b in UserCode)
{
UserCode[index] = 0x20;
index++;
}
Это работает нормально, но я уверен, что должен быть более простой способ сделать это. Когда в конструкторе массив устанавливается в UserCode = new byte[6]
, массив байтов автоматически инициализируется нулевыми значениями по умолчанию. Есть ли способ сделать так, чтобы при объявлении он становился пробелами, чтобы при вызове конструктора моего класса он сразу инициализировался подобным образом? Или какой-нибудь memset
-подобной функцией?
Для небольших массивов использовать синтаксис инициализации массива:
var sevenItems = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
Для больших массивов использовать стандартный для
петли. Это самый читаемый и эффективный способ сделать это:
var sevenThousandItems = new byte[7000];
for (int i = 0; i < sevenThousandItems.Length; i++)
{
sevenThousandItems[i] = 0x20;
}
Конечно, если вам нужно для этого много, тогда можно создать вспомогательный метод, чтобы помочь сохранить ваш лаконичный код:
byte[] sevenItems = CreateSpecialByteArray(7);
byte[] sevenThousandItems = CreateSpecialByteArray(7000);
// ...
public static byte[] CreateSpecialByteArray(int length)
{
var arr = new byte[length];
for (int i = 0; i < arr.Length; i++)
{
arr[i] = 0x20;
}
return arr;
}
Используйте это для создания массива в первую очередь:
byte[] array = Enumerable.Repeat((byte)0x20, <number of elements>).ToArray();
Замените <количество элементов>
на желаемый размер массива.
Вы можете использовать Enumerable.Repeat()
Массив из 100 элементов, инициализированный в 0x20:
byte[] arr1 = Enumerable.Repeat(0x20,100).ToArray();
var array = Encoding.ASCII.GetBytes(new string(' ', 100));
Если вам нужно инициализировать небольшого массива можно использовать:
byte[] smallArray = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };
Если у вас большой массив, то вы могли бы использовать:
byte[] bitBiggerArray Enumerable.Repeat(0x20, 7000).ToArray();
Который является простым и легким для следующего парня/девушку, чтобы читать. И будет достаточно быстро 99,9% времени. (Обычно это будет оптимальный вариант™)
Однако если вам действительно очень нужна супер скорость, взывая к оптимизированным методом остановлюсь, с использованием P/Invoke для, это для вас: (Здесь завернутый в приятный для использования класса)
public static class Superfast
{
[DllImport("msvcrt.dll",
EntryPoint = "memset",
CallingConvention = CallingConvention.Cdecl,
SetLastError = false)]
private static extern IntPtr MemSet(IntPtr dest, int c, int count);
//If you need super speed, calling out to M$ memset optimized method using P/invoke
public static byte[] InitByteArray(byte fillWith, int size)
{
byte[] arrayBytes = new byte[size];
GCHandle gch = GCHandle.Alloc(arrayBytes, GCHandleType.Pinned);
MemSet(gch.AddrOfPinnedObject(), fillWith, arrayBytes.Length);
return arrayBytes;
}
}
Использование:
byte[] oneofManyBigArrays = Superfast.InitByteArray(0x20,700000);
Ребята, прежде чем я дал вам свой ответ. Я просто хочу указать на ваши неправильного использования цикла foreach. Видишь ли, поскольку вы должны инкрементировать стандарт на " индекса;петлю" и станет не только более компактной, но и более эффективный (на"объекту" и делает много вещей под капотом):
for (int index = 0; index < UserCode.Length; ++index)
{
UserCode[index] = 0x20;
}
Самый быстрый способ сделать это, чтобы использовать API:
бр = значение 0xFF;
RtlFillMemory(параметр pbuffer, nFileLen, бр);
используя указатель на буфер, длины писать, и закодированных байтов. Я думаю, что самый быстрый способ сделать это в управляемом коде (намного медленнее), чтобы создать небольшой блок инициализации байт, а затем использовать буфер.Blockcopy, чтобы записать их в массив в цикле. Я бросил это вместе, но не'т проверял, но вы получите идею:
long size = GetFileSize(FileName);
// zero byte
const int blocksize = 1024;
// 1's array
byte[] ntemp = new byte[blocksize];
byte[] nbyte = new byte[size];
// init 1's array
for (int i = 0; i < blocksize; i++)
ntemp[i] = 0xff;
// get dimensions
int blocks = (int)(size / blocksize);
int remainder = (int)(size - (blocks * blocksize));
int count = 0;
// copy to the buffer
do
{
Buffer.BlockCopy(ntemp, 0, nbyte, blocksize * count, blocksize);
count++;
} while (count < blocks);
// copy remaining bytes
Buffer.BlockCopy(ntemp, 0, nbyte, blocksize * count, remainder);
Это быстрая версия код из пост помечен как ответ.
Все тесты, которые я провел, показывают, что простой на петли, которая содержит только что-то вроде массива, заполнение как правило, в два раза быстрее, если она уменьшится за против, если он инкрементируется.
Кроме того, длина массива собственность уже передали в качестве параметра, так что это не'т должны быть получены из свойств массива. Он также должен быть предварительно рассчитывается и присваивается локальной переменной. Расчеты границ цикла, которые включают аксессу собственность будет повторно вычислить значение границы перед началом каждой итерации цикла.
public static byte[] CreateSpecialByteArray(int length)
{
byte[] array = new byte[length];
int len = length - 1;
for (int i = len; i >= 0; i--)
{
array[i] = 0x20;
}
return array;
}
Просто, чтобы развернуть на мой ответ аккуратнее способ сделать это несколько раз, вероятно, будет:
PopulateByteArray(UserCode, 0x20);
который призывает:
public static void PopulateByteArray(byte[] byteArray, byte value)
{
for (int i = 0; i < byteArray.Length; i++)
{
byteArray[i] = value;
}
}
Это имеет то преимущество, хорошая эффективная для петли (упоминание gwiazdorrr'ы ответ), а также приятный опрятный внешний вид звонить, если он используется много. И гораздо больше на первый взгляд легче, чем перечисление, кого я лично думаю. :)
Эта функция намного быстрее, чем цикл для заполнения массива.
Массив.Команда copy-это очень быстрая функция копирования памяти. Эта функция использует, что постоянно называя массива.Команды "копировать" и удвоения размера того, что мы копируем пока массив не будет полностью.
Я обсуждаю это на моем блоге в http://coding.grax.com/2013/06/fast-array-fill-function-revisited.html
Обратите внимание, что это можно будет превратить в метод расширения, просто добавив слово "эта" на способ заявления, т. е. общественная статический недействительным ArrayFill в<Т>(Это Т[] arrayToFill ...
public static void ArrayFill<T>(T[] arrayToFill, T fillValue)
{
// if called with a single value, wrap the value in an array and call the main function
ArrayFill(arrayToFill, new T[] { fillValue });
}
public static void ArrayFill<T>(T[] arrayToFill, T[] fillValue)
{
if (fillValue.Length >= arrayToFill.Length)
{
throw new ArgumentException("fillValue array length must be smaller than length of arrayToFill");
}
// set the initial array value
Array.Copy(fillValue, arrayToFill, fillValue.Length);
int arrayToFillHalfLength = arrayToFill.Length / 2;
for (int i = fillValue.Length; i < arrayToFill.Length; i *= 2)
{
int copyLength = i;
if (i > arrayToFillHalfLength)
{
copyLength = arrayToFill.Length - i;
}
Array.Copy(arrayToFill, 0, arrayToFill, i, copyLength);
}
}
Вы можете использовать инициализатор коллекции:
UserCode = new byte[]{0x20,0x20,0x20,0x20,0x20,0x20};
Это будет работать лучше, чем Repeat
, если значения не идентичны.
Вы могли бы ускорить инициализацию и упростить код с помощью параллельного класса (.Net 4 и новее):
public static void PopulateByteArray(byte[] byteArray, byte value)
{
Parallel.For(0, byteArray.Length, i => byteArray[i] = value);
}
Конечно, вы можете создать массив в то же время:
public static byte[] CreateSpecialByteArray(int length, byte value)
{
var byteArray = new byte[length];
Parallel.For(0, length, i => byteArray[i] = value);
return byteArray;
}