获取指向KeyValuePair

本文关键字:不安全 指针 数组 decimal 取指 KeyValuePair DateTime 获取 | 更新日期: 2023-09-27 18:15:20

我有KeyValuePair<DateTime,decimal>的大数组。我知道在内存中数组是连续的,因为KVP是一个值类型,DateTime实际上是一个Int64,而decimal是一个包含4个int的数组(这不会改变)。然而,DateTime不是位元化的,十进制也不是原始的。

是否有任何方法来滥用类型系统并获得指向数组的不安全指针并将其作为字节使用?(GCHandle.Alloc不能处理这两种类型,当它们是结构的一部分时,但可以处理这些类型的数组。)

(如果您对为什么感兴趣,我现在手动将数组转换为我认为是1对1的字节[]表示,并且速度很慢)

获取指向KeyValuePair<DateTime,decimal数组的不安全指针;在c#

最后,还有一个公共工具:System.Runtime.CompilerServices.Unsafe包。

下面是一个合格的测试:

using System.Runtime.CompilerServices.Unsafe;
[Test]
public unsafe void CouldUseNewUnsafePackage() {
    var dt = new KeyValuePair<DateTime, decimal>[2];
    dt[0] = new KeyValuePair<DateTime, decimal>(DateTime.UtcNow.Date, 123.456M);
    dt[1] = new KeyValuePair<DateTime, decimal>(DateTime.UtcNow.Date.AddDays(1), 789.101M);
    var obj = (object)dt;
    byte[] asBytes = Unsafe.As<byte[]>(obj);
    //Console.WriteLine(asBytes.Length); // prints 2
    fixed (byte* ptr = &asBytes[0]) {
        // reading this: https://github.com/dotnet/coreclr/issues/5870
        // it looks like we could fix byte[] and actually KeyValuePair<DateTime, decimal> will be fixed
        // because:
        // "GC does not care about the exact types, e.g. if type of local object 
        // reference variable is not compatible with what is actually stored in it, 
        // the GC will still track it fine."
        for (int i = 0; i < (8 + 16) * 2; i++) {
            Console.WriteLine(*(ptr + i));
        }
        var firstDate = *(DateTime*)ptr;
        Assert.AreEqual(DateTime.UtcNow.Date, firstDate);
        Console.WriteLine(firstDate);
        var firstDecimal = *(decimal*)(ptr + 8);
        Assert.AreEqual(123.456M, firstDecimal);
        Console.WriteLine(firstDecimal);
        var secondDate = *(DateTime*)(ptr + 8 + 16);
        Assert.AreEqual(DateTime.UtcNow.Date.AddDays(1), secondDate);
        Console.WriteLine(secondDate);
        var secondDecimal = *(decimal*)(ptr + 8 + 16 + 8);
        Assert.AreEqual(789.101M, secondDecimal);
        Console.WriteLine(secondDecimal);
    }
}

我刚刚测试了unsafeGCHandle.Alloc不工作(如您所建议的)。仍然有一个非常不安全的黑客来做这个。我不知道这对当前的CLR是否安全。这当然不能保证将来能工作。

您可以将任意类型的对象引用转换为IL中的任何其他引用类型。该IL将无法验证。JIT倾向于接受相当多的不可验证的构造。也许这是因为他们想要支持Managed c++。

因此,您需要生成一个DynamicMethod,它大致具有以下IL:
static T UnsafeCast(object value) {
 ldarg.1 //load type object
 ret //return type T
}

我认为这应该可以…

或者,您可以使用反射调用System.Runtime.CompilerServices.JitHelpers.UnsafeCast<T>

这是个危险的工具…我不会在生产代码中使用它