Boxing экземпляра типа, определяемого во время run-time'а

Возможно ли вообще с помощью этого:

  • ReadOnlySpan<byte> data – данные структуры
  • Type type – тип структуры

создать object, c типом type и данными data?


Для примера возьмём int:

object Box(ReadOnlySpan<byte> data, Type type)
{
    <реализация>
}
object Box<T>(T value) where T : struct => (object)value;

Этот код:

Span<byte> data = stackalloc byte[4];
BinaryPrimitives.WriteInt32BigEndian(data, 15); // осторожно: вы можете не угадать с endian: тут он используется в демонтративных целях
var type = typeof(int);

var boxedInt32 = Box(data, type);

…будет такой же, как и этот код:

var int32 = 15;

var boxedInt32 = Box(int32); // (object)int32

…за одним исключением: в первом случае мы определяем тип во время run-time'а, а во втором – во время компиляции.


У меня есть наводка по этому поводу:

object Box(ReadOnlySpan<byte> data, Type type)
{
    // checks skipped

    var obj = Activator.CreateInstance(type);
    ref var objRef = ref Unsafe.As<object, byte>(ref obj);
    ref var objDataRef = ref Unsafe.Add(ref objRef, IntPtr.Size + 4); // type handle (размер указателя) + sync block index (4 байт)
    var objDataSize = Marshal.SizeOf(obj);
    var objData = MemoryMarshal.CreateSpan(ref objDataRef, objDataSize);
    data.CopyTo(objData);

    return obj;
}

…но я не знаю, на сколько данный метод безопасен и подойдёт ли он для разных ОС.


Примечание: Мне просто интересно, возможно ли вообще такое?

Ответы (1 шт):

Автор решения: MSDN.WhiteKnight

но я не знаю, на сколько данный метод безопасен

На столько же, насколько безопасен любой метод, пытающийся взломать внутренние структуры CLR - примерно ненасколько. Для частного случая, когда type является примитивным типом или состоит только из примитивных типов и имеет атрибут StructLayout со значением LayoutKind.Sequential или LayoutKind.Explicit, существует такое решение, основанное на документированных API:

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;

class Program
{
    [StructLayout(LayoutKind.Sequential)]
    struct S
    {
        public byte x;
        public byte y;

        public override string ToString()
        {
            return x.ToString() + ";" + y.ToString();
        }
    }

    static object Box(ReadOnlySpan<byte> data, Type type)
    {

        var obj = Activator.CreateInstance(type);
        GCHandle gch = GCHandle.Alloc(obj, GCHandleType.Pinned);
        try
        {
            IntPtr p = gch.AddrOfPinnedObject();
            Marshal.Copy(data.ToArray(), 0, p, data.Length);
        }
        finally { gch.Free(); }
        return obj;
    }

    static void Main(string[] args)
    {
        object x = Box(new byte[] { 1, 2 }, typeof(S));
        Console.WriteLine(x); //Результат: 1;2

        Console.ReadKey();
    }
}

В общем случае, насколько я знаю, нет, стабильного решения не существует.

→ Ссылка