将字节数组初始化为特定值,而不是默认 null

本文关键字:null 默认 字节数 字节 数组 初始化 | 更新日期: 2023-09-27 17:57:59

我正忙着将一个用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] 时,字节数组将自动初始化为默认 null 值。有没有办法让它在声明时变成所有空格,这样当我调用我的类的构造函数时,它会像这样立即初始化?还是一些类似memset的功能?

将字节数组初始化为特定值,而不是默认 null

对于小数组,请使用数组初始化语法:

var sevenItems = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };

对于较大的数组,请使用标准for循环。这是最易读和最有效的方法:

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();

<number of elements>替换为所需的数组大小。

您可以使用

Enumerable.Repeat((

Enumerable.Repeat生成一个包含一个重复值的序列。

初始化为 0x20 的 100 个项目的数组:

byte[] arr1 = Enumerable.Repeat((byte)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 调用优化的 memset 方法:(这里总结在一个很好用的类中(

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);
        gch.Free();
        return arrayBytes;
    }
}

用法:

byte[] oneofManyBigArrays =  Superfast.InitByteArray(0x20,700000);

也许这些可能会有所帮助?

C# 中的内存集等效于什么?

http://techmikael.blogspot.com/2009/12/filling-array-with-default-value.html

我之前的家伙给了你答案。我只想指出你对foreach循环的滥用。看,由于你必须增加索引标准"for loop"不仅更紧凑,而且更有效("foreach"在引擎盖下做很多事情(:

for (int index = 0; index < UserCode.Length; ++index)
{
    UserCode[index] = 0x20;
}

这是标记为答案的帖子中代码的更快版本。

我执行的所有基准测试都表明,仅包含数组填充之类的内容的简单 for 循环在递减时通常比递增时快两倍

此外,数组 Length 属性已作为参数传递,因此不需要从数组属性中检索它。 它还应该预先计算并分配给局部变量。涉及属性访问器的循环边界计算将在每次循环迭代之前重新计算边界的值。

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;
    }
}

这有一个很好的高效for循环(提到gwiazdorrr的答案(的优点,如果它被大量使用,还有一个漂亮的整洁调用。而且一目了然的可读性比我个人认为的枚举要多得多。:)

最快的方法是使用 api:

bR = 0xFF;

RtlFillMemory(pBuffer, nFileLen, bR(;

使用指向缓冲区的指针、写入长度和编码字节。我认为在托管代码中执行此操作的最快方法是创建一小块初始化字节,然后使用 Buffer.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);

这个函数比填充数组的 for 循环快得多。

Array.Copy 命令是一个非常快速的内存复制函数。 此函数通过重复调用 Array.Copy 命令并将我们复制的大小加倍,直到数组已满来利用这一点。

我在 https://grax32.com/2013/06/fast-array-fill-function-revisited.html 的博客上讨论这个问题(链接更新于12/16/2019(。 另请参阅提供此扩展方法的 Nuget 包。 http://sites.grax32.com/ArrayExtensions/

请注意,只需在方法声明中添加单词"this"即可轻松将其转换为扩展方法,即 public static void ArrayFill<T>(this T[] 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;
}