gpt4 book ai didi

c# - 将十进制复制到字节数组而不分配

转载 作者:行者123 更新时间:2023-12-04 02:29:51 25 4
gpt4 key购买 nike

梦想
我正在寻找一种将十进制值复制到字节数组缓冲区中的方法,然后能够在没有任何堆分配的情况下将这些字节读回十进制。理想情况下,这不需要不安全的上下文。
我的尝试
我以前在 C# 中使用临时联合来做一些疯狂的事情。这是一种以您想要的任何随机方式读取内存的非常酷的方式,但您必须小心。你可以进入一个变量明确存在的损坏状态,例如 byte[]但在调试器中查看的值是 int[] .我什至不知道这样的事情是可能的!
注意:Marc 在下面的评论中提出了一个非常重要的观点。由于字节顺序,您无法使用像这样的重叠结构概念可靠地将数字直接转换为字节。在这种情况下,您可以安全地使用整数,因为十进制类型在内部使用了 4 个整数。这是来自 protobuf-net 的十进制序列化程序的 [示例]。
1: struct union w/decimal 和 byte[]
第一次尝试尝试使用带有 decimal 的结构联合概念。和 byte[]字段,都在 0 偏移处,因此它们占用完全相同的内存位置。然后我可以写入一个字段并从另一个字段读取。

[StructLayout(LayoutKind.Explicit)]
private readonly struct convert_decimal_to_bytes
{
[FieldOffset(0)]
public readonly decimal value;
[FieldOffset(0)]
public readonly byte[] bytes;

public convert_decimal_to_bytes(decimal value)
{
bytes = default;
this.value = value;
}
}

这甚至不会在没有抛出的情况下运行 - 类型无法加载以下内容:
System.TypeLoadException : Could not load type 'convert_decimal_to_bytes' from assembly 'teloneum, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' because it contains an object field at offset 0 that is incorrectly aligned or overlapped by a non-object field.
显然 CLR 不喜欢它们被重叠——但这只是因为一个是值类型而另一个是引用类型。
2: struct union w/十进制和 16 个字节!
所以我决定去掉引用类型!现在,以下工作,或者至少,它会工作,但顺序全都搞砸了。我当然可以使用一个大的十进制数,在这里运行它,然后通过字段偏移量进行生成,使其与我的测试相匹配,但这似乎是错误的——此外,我尝试过的每个十进制数中至少有三个零字节:) 也许我最后只是懒惰了……毕竟已经晚了。
[StructLayout(LayoutKind.Explicit)]
private readonly struct convert_decimal_to_bytes
{
[FieldOffset(0)] public readonly decimal value;

[FieldOffset( 0)] public readonly byte byte_1;
[FieldOffset( 1)] public readonly byte byte_2;
[FieldOffset( 2)] public readonly byte byte_3;
[FieldOffset( 3)] public readonly byte byte_4;
[FieldOffset( 4)] public readonly byte byte_5;
[FieldOffset( 5)] public readonly byte byte_6;
[FieldOffset( 6)] public readonly byte byte_7;
[FieldOffset( 7)] public readonly byte byte_8;
[FieldOffset( 8)] public readonly byte byte_9;
[FieldOffset( 9)] public readonly byte byte_10;
[FieldOffset(10)] public readonly byte byte_11;
[FieldOffset(11)] public readonly byte byte_12;
[FieldOffset(12)] public readonly byte byte_13;
[FieldOffset(13)] public readonly byte byte_14;
[FieldOffset(14)] public readonly byte byte_15;
[FieldOffset(15)] public readonly byte byte_16;

public convert_decimal_to_bytes(decimal value)
{
byte_1 = default;
byte_2 = default;
byte_3 = default;
byte_4 = default;
byte_5 = default;
byte_6 = default;
byte_7 = default;
byte_8 = default;
byte_9 = default;
byte_10 = default;
byte_11 = default;
byte_12 = default;
byte_13 = default;
byte_14 = default;
byte_15 = default;
byte_16 = default;
this.value = value;
}

public convert_decimal_to_bytes(int startIndex, byte[] buffer)
{
value = default;
byte_1 = buffer[startIndex++];
byte_2 = buffer[startIndex++];
byte_3 = buffer[startIndex++];
byte_4 = buffer[startIndex++];
byte_5 = buffer[startIndex++];
byte_6 = buffer[startIndex++];
byte_7 = buffer[startIndex++];
byte_8 = buffer[startIndex++];
byte_9 = buffer[startIndex++];
byte_10 = buffer[startIndex++];
byte_11 = buffer[startIndex++];
byte_12 = buffer[startIndex++];
byte_13 = buffer[startIndex++];
byte_14 = buffer[startIndex++];
byte_15 = buffer[startIndex++];
byte_16 = buffer[startIndex];
}


public static void Copy(decimal value, int startIndex, byte[] buffer)
{
var convert = new convert_decimal_to_bytes(value);

buffer[startIndex++] = convert.byte_1;
buffer[startIndex++] = convert.byte_2;
buffer[startIndex++] = convert.byte_3;
buffer[startIndex++] = convert.byte_4;
buffer[startIndex++] = convert.byte_5;
buffer[startIndex++] = convert.byte_6;
buffer[startIndex++] = convert.byte_7;
buffer[startIndex++] = convert.byte_8;
buffer[startIndex++] = convert.byte_9;
buffer[startIndex++] = convert.byte_10;
buffer[startIndex++] = convert.byte_11;
buffer[startIndex++] = convert.byte_12;
buffer[startIndex++] = convert.byte_13;
buffer[startIndex++] = convert.byte_14;
buffer[startIndex++] = convert.byte_15;
buffer[startIndex] = convert.byte_16;
}

public static decimal Read(int startIndex, byte[] buffer)
{
var convert = new convert_decimal_to_bytes(startIndex, buffer);

return convert.value;
}
}

最佳答案

在 .NET 5.0 之前,如果没有一些丑陋的黑客,这很尴尬。从 .NET 5.0 开始,有更多方法接受跨度。
您可以使用 GetBits(decimal d, Span<int>) 使用堆栈分配范围的方法,然后根据需要将四个整数转换为现有的字节数组,例如与 BitConverter.TryWriteBytes .
在另一个方向,有一个 Decimal(ReadOnlySpan<int>)构造函数,所以你可以再次 stackalloc 一个 Span<int> , 使用 BitConverter.ToInt32(ReadOnlySpan<byte>)重复从字节数组填充该跨度,并将其传递给构造函数。
顺便说一句,您可能希望通过代码库更广泛地接受跨度,而不是接受字节数组和起始索引。
这是一些完成上述所有操作的示例代码 - 很可能可以稍微更有效地完成它,但希望这能传达这个想法,并且这确实避免了分配:

using System;

class Program
{
public static void Copy(decimal value, int startIndex, byte[] buffer)
{
Span<int> int32s = stackalloc int[4];
decimal.GetBits(value, int32s);

var bufferSpan = buffer.AsSpan();
for (int i = 0; i < 4; i++)
{
// These slices are bigger than we need, but this is the simplest approach.
var slice = bufferSpan.Slice(startIndex + i * 4);
if (!BitConverter.TryWriteBytes(slice, int32s[i]))
{
throw new ArgumentException("Not enough space in span");
}
}
}

public static decimal Read(int startIndex, byte[] buffer)
{
Span<int> int32s = stackalloc int[4];
ReadOnlySpan<byte> bufferSpan = buffer.AsSpan();
for (int i = 0; i < 4; i++)
{
var slice = bufferSpan.Slice(startIndex + i * 4);
int32s[i] = BitConverter.ToInt32(slice);
}
return new decimal(int32s);
}

static void Main()
{
byte[] bytes = new byte[16];
decimal original = 1234.567m;
Copy(original, 0, bytes);
decimal restored = Read(0, bytes);
Console.WriteLine(restored);
}
}

或使用 MemoryMarshal 做同样的事情:
public static void Copy(decimal value, int startIndex, byte[] buffer)
=> decimal.GetBits(value, MemoryMarshal.Cast<byte, int>(buffer.AsSpan(startIndex)));

public static decimal Read(int startIndex, byte[] buffer)
=> new decimal(MemoryMarshal.Cast<byte, int>(buffer.AsSpan(startIndex)));

关于c# - 将十进制复制到字节数组而不分配,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/64906392/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com