c# - 为什么 "decimal"数据类型不可 blittable?

标签 c# .net memory-management

GCHandle.Alloc 拒绝用包含“十进制”数据类型的结构固定数组,但同时可以很好地处理“ double ”。这是什么原因,我可以通过某种方式解决它吗?

我知道我可以改用 unsafe/fixed 来获取指向数组的指针,但这不适用于泛型。 :-(

演示问题的完整示例代码。第一个 Alloc 有效,但第二个失败并显示

Object contains non-primitive or non-blittable data.

    public struct X1
    {
        public double X;
    }

    public struct X2
    {
        public decimal X;
    }

现在试试这个:

        var x1 = new[] {new X1 {X = 42}};
        var handle1 = GCHandle.Alloc(x1, GCHandleType.Pinned); // Works
        var x2 = new[] { new X2 { X = 42 } };
        var handle2 = GCHandle.Alloc(x2, GCHandleType.Pinned); // Fails

最佳答案

  var handle2 = GCHandle.Alloc(x2, GCHandleType.Pinned);

运行时硬性假设您将调用 handle.AddrOfPinnedObject()。当然你是,否则没有理由分配固定句柄。这将返回一个 非托管指针,一个 C# 中的 IntPtr。与托管指针不同,您使用 fixed 关键字获得的那种指针。

它进一步假设您要将此指针传递给关心值大小和表示的代码。但否则无法注入(inject)转换,该代码将直接在 IntPtr 上参与。这要求值类型是blittable,这是一个令人讨厌的词,意味着值中的字节可以简单地解释或直接复制而无需任何转换,而且使用 IntPtr 的代码很有可能会继续能够正确识别值。

这是一些 .NET 类型的问题,例如 bool 类型就臭名昭著。只需使用 bool 而不是 decimal 尝试相同的代码,就会发现您会得到完全相同的异常。 System.Boolean 是一种非常困难的互操作类型,没有描述它应该是什么样子的主导标准。它在 C 语言和 Winapi 中为 4 个字节,在 COM Automation 中为 2 个字节,在 C++ 和其他几种语言中为 1 个字节。换句话说,“其他代码”尝试解释 1 字节 .NET 值的可能性很小。不可预测的大小尤其令人讨厌,它会甩掉所有后续成员。

与 System.Decimal 非常相似,没有广泛采用的标准来确定其内部格式。许多语言根本不支持它,尤其是 C 和 C++,如果您使用这种语言编写代码,则需要使用库。哪个可能使用IEEE 754-2008小数,但这是一个约翰尼来晚了,并遭受“太多标准”的问题。在编写 CLI 规范时,IEEE 854-1987 标准已经存在,但它被广泛忽视。今天仍然是一个问题,支持小数的处理器设计很少,我只知道 PowerPC。

长话短说,您需要创建自己的 blittable 类型来存储小数。 .NET 设计者决定使用 COM Automation Currency 类型来实现 System.Decimal,这是当时由于 Visual Basic 而占主导地位的实现。这是极不可能改变的,太多的代码依赖于内部格式,使得这段代码最有可能兼容和快速:

    public struct X2 {
        private long nativeDecimal;
        public decimal X {
            get { return decimal.FromOACurrency(nativeDecimal); }
            set { nativeDecimal = decimal.ToOACurrency(value); }
        }
    }

你也可以考虑 uint[] 和 Decimal.Get/SetBits() 但我认为它不太可能会更快,你必须尝试。

关于c# - 为什么 "decimal"数据类型不可 blittable?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30213132/

相关文章:

java - Android OutOfMemoryError - 加载 JSON 文件

跨 IPC 的 C# 事件

c# - 没有 App.config 的 SQLite Entity Framework

c++ - 是否可以删除非新对象?

asp.net - 将 dotless 添加到我的本地网站后出现 HTTP 错误 500.23

c# - 如何允许多种类型的部署?

c++ - C++中的复杂内存管理问题

c# - 没有键/值文本的 POST JSON 字典

c# - 在MVC项目中使用微软账号认证时如何获取用户邮箱?

c# - 为什么我不能在此三元运算中将 int 转换为字符串