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 шт):
но я не знаю, на сколько данный метод безопасен
На столько же, насколько безопасен любой метод, пытающийся взломать внутренние структуры 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();
}
}
В общем случае, насколько я знаю, нет, стабильного решения не существует.