当前位置:  开发笔记 > 编程语言 > 正文

C#不安全值类型数组到字节数组转换

如何解决《C#不安全值类型数组到字节数组转换》经验,为你挑选了1个好方法。

我使用扩展方法将float数组转换为字节数组:

public static unsafe byte[] ToByteArray(this float[] floatArray, int count)
{
    int arrayLength = floatArray.Length > count ? count : floatArray.Length;
    byte[] byteArray = new byte[4 * arrayLength];
    fixed (float* floatPointer = floatArray)
    {
        fixed (byte* bytePointer = byteArray)
        {
            float* read = floatPointer;
            float* write = (float*)bytePointer;
            for (int i = 0; i < arrayLength; i++)
            {
                *write++ = *read++;
            }
        }
    }
    return byteArray;
}

我知道数组是指向与元素类型和数量信息相关的内存的指针.此外,在我看来,如果没有像上面那样复制数据,就没有办法从字节数组转换到字节数组.

我明白了吗?甚至不可能编写IL来从指针,类型和长度创建数组而不复制数据?

编辑:谢谢你的答案,我学到了一些基础知识,并尝试了新的技巧!

在最初接受Davy Landman的回答之后,我发现虽然他出色的StructLayout hack确实将字节数组转换为浮点数组,但它却无法正常工作.展示:

[StructLayout(LayoutKind.Explicit)]
struct UnionArray
{
    [FieldOffset(0)]
    public Byte[] Bytes;

    [FieldOffset(0)]
    public float[] Floats;
}

static void Main(string[] args)
{
    // From bytes to floats - works
    byte[] bytes = { 0, 1, 2, 4, 8, 16, 32, 64 };
    UnionArray arry = new UnionArray { Bytes = bytes };
    for (int i = 0; i < arry.Bytes.Length / 4; i++)
        Console.WriteLine(arry.Floats[i]);

    // From floats to bytes - index out of range
    float[] floats = { 0.1f, 0.2f, 0.3f };
    arry = new UnionArray { Floats = floats };
    for (int i = 0; i < arry.Floats.Length * 4; i++)
        Console.WriteLine(arry.Bytes[i]);
}

似乎CLR将两个阵列视为具有相同的长度.如果结构是从float数据创建的,则字节数组的长度太短.



1> Omer Mor..:

你可以使用一个非常丑陋的黑客来使用内存操作临时将你的数组更改为byte [].

这非常快速有效,因为它不需要克隆数据并对其进行迭代.

我在32位和64位操作系统中测试了这个hack,所以它应该是可移植的.

源代码+样本使用情况保留在https://gist.github.com/1050703,但为方便起见,我也将其粘贴在此处:

public static unsafe class FastArraySerializer
{
    [StructLayout(LayoutKind.Explicit)]
    private struct Union
    {
        [FieldOffset(0)] public byte[] bytes;
        [FieldOffset(0)] public float[] floats;
    }

    [StructLayout(LayoutKind.Sequential, Pack = 1)]
    private struct ArrayHeader
    {
        public UIntPtr type;
        public UIntPtr length;
    }

    private static readonly UIntPtr BYTE_ARRAY_TYPE;
    private static readonly UIntPtr FLOAT_ARRAY_TYPE;

    static FastArraySerializer()
    {
        fixed (void* pBytes = new byte[1])
        fixed (void* pFloats = new float[1])
        {
            BYTE_ARRAY_TYPE = getHeader(pBytes)->type;
            FLOAT_ARRAY_TYPE = getHeader(pFloats)->type;
        }
    }

    public static void AsByteArray(this float[] floats, Action action)
    {
        if (floats.handleNullOrEmptyArray(action)) 
            return;

        var union = new Union {floats = floats};
        union.floats.toByteArray();
        try
        {
            action(union.bytes);
        }
        finally
        {
            union.bytes.toFloatArray();
        }
    }

    public static void AsFloatArray(this byte[] bytes, Action action)
    {
        if (bytes.handleNullOrEmptyArray(action)) 
            return;

        var union = new Union {bytes = bytes};
        union.bytes.toFloatArray();
        try
        {
            action(union.floats);
        }
        finally
        {
            union.floats.toByteArray();
        }
    }

    public static bool handleNullOrEmptyArray(this TSrc[] array, Action action)
    {
        if (array == null)
        {
            action(null);
            return true;
        }

        if (array.Length == 0)
        {
            action(new TDst[0]);
            return true;
        }

        return false;
    }

    private static ArrayHeader* getHeader(void* pBytes)
    {
        return (ArrayHeader*)pBytes - 1;
    }

    private static void toFloatArray(this byte[] bytes)
    {
        fixed (void* pArray = bytes)
        {
            var pHeader = getHeader(pArray);

            pHeader->type = FLOAT_ARRAY_TYPE;
            pHeader->length = (UIntPtr)(bytes.Length / sizeof(float));
        }
    }

    private static void toByteArray(this float[] floats)
    {
        fixed(void* pArray = floats)
        {
            var pHeader = getHeader(pArray);

            pHeader->type = BYTE_ARRAY_TYPE;
            pHeader->length = (UIntPtr)(floats.Length * sizeof(float));
        }
    }
}

用法是:

var floats = new float[] {0, 1, 0, 1};
floats.AsByteArray(bytes =>
{
    foreach (var b in bytes)
    {
        Console.WriteLine(b);
    }
});


这里有一个更新,更便携的"一年后"版本的答案:http://stackoverflow.com/a/3577253/11545.@Omer - 也许更新这个答案?
推荐阅读
吻过彩虹的脸_378
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有