将字节数组初始化为某个值,而不是默认的null?

时间:2011-05-27 09:08:11

标签: c# .net arrays initialization

我正在忙于将用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 - 类似函数?

13 个答案:

答案 0 :(得分:163)

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

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

答案 1 :(得分:82)

首先使用它来创建数组:

byte[] array = Enumerable.Repeat((byte)0x20, <number of elements>).ToArray();

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

答案 2 :(得分:30)

您可以使用Enumerable.Repeat()

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

byte[] arr1 = Enumerable.Repeat(0x20,100).ToArray();

答案 3 :(得分:28)

var array = Encoding.ASCII.GetBytes(new string(' ', 100));

答案 4 :(得分:9)

如果您需要初始化一个小数组,可以使用:

byte[] smallArray = new byte[] { 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 };

如果你有一个更大的数组,那么你可以使用:

byte[] bitBiggerArray Enumerable.Repeat(0x20, 7000).ToArray();

这对于下一个男/女孩来说很简单,也很容易阅读。并且99.9%的时间足够快。 (通常是BestOption™)

但是如果你真的需要超级速度,那么使用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);
        return arrayBytes;
    }
}

用法:

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

答案 5 :(得分:4)

答案 6 :(得分:4)

我之前的伙伴们给了你答案。我只是想指出你滥用foreach循环。看,因为你必须增加索引标准“for loop”不仅更紧凑,而且效率更高(“foreach”做了很多事情):

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

答案 7 :(得分:4)

最快的方法是使用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);

答案 8 :(得分:3)

为了扩展我的答案,多次这样做的简洁方法可能是:

PopulateByteArray(UserCode, 0x20);

调用:

public static void PopulateByteArray(byte[] byteArray, byte value)
{
    for (int i = 0; i < byteArray.Length; i++)
    {
        byteArray[i] = value;
    }
}

这有一个很好的循环效率(提到gwiazdorrr的答案)以及如果它被大量使用的漂亮整洁的调用。与我个人认为的枚举相比,一瞥可读。 :)

答案 9 :(得分:2)

您可以使用collection initializer

UserCode = new byte[]{0x20,0x20,0x20,0x20,0x20,0x20};

如果值不相同,这将比Repeat更好。

答案 10 :(得分:2)

此函数比for循环更快地填充数组。

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

我在http://coding.grax.com/2013/06/fast-array-fill-function-revisited.html

的博客上讨论了这个问题

请注意,只需在方法声明中添加单词“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);
    }
}

答案 11 :(得分:2)

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

我所执行的所有基准测试表明,只包含类似数组填充的简单的 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;
}

答案 12 :(得分:1)

使用Parallel类(.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;
}