Мне нужно написать свободный код для выделения, который позволяет избежать упаковки при приведении из универсального c параметра T со структурными ограничениями к интерфейсу для доступа к одному реализованному свойству.
Ранее я решил эту проблему, используя динамически сгенерированный код и дерево лямбда-выражений, например:
public delegate void SetEGIDWithoutBoxingActionCast<T>(ref T target, EGID egid) where T : struct, IEntityComponent;
static SetEGIDWithoutBoxingActionCast<T> MakeSetter()
{
if (ComponentBuilder<T>.HAS_EGID)
{
Type myTypeA = typeof(T);
PropertyInfo myFieldInfo = myTypeA.GetProperty("ID");
ParameterExpression targetExp = Expression.Parameter(typeof(T).MakeByRefType(), "target");
ParameterExpression valueExp = Expression.Parameter(typeof(EGID), "value");
MemberExpression fieldExp = Expression.Property(targetExp, myFieldInfo);
BinaryExpression assignExp = Expression.Assign(fieldExp, valueExp);
var setter = Expression.Lambda<SetEGIDWithoutBoxingActionCast<T>>(assignExp, targetExp, valueExp).Compile();
return setter;
}
Полученный в результате делегат "установщика" позволит установить значение свойства для любой структуры, реализующей свойство ID без каких-либо упаковок.
Однако теперь мне нужно решить ту же проблему без генерации динамического c кода. Я провел несколько быстрых экспериментов, но кажется, что Небезопасный класс не может преобразовать структуру в интерфейс, используя любой из As методов. Возможно, потому что структура не может быть приведена непосредственно к интерфейсу, поскольку она рассматривается как объект, и, следовательно, не может обрабатываться непосредственно указателем.
Любой указатель (без каламбура)?
Править : Мне не обязательно приводить структуру к интерфейсу, мне просто нужно иметь возможность записать значение в идентификатор свойства. Тот факт, что это свойство, а не поле, не поможет наверняка (поскольку я нашел некоторый код, который может вычислить смещение поля в структуре, но, конечно, не может использоваться для свойства)
Ответ принят, окончательный код такой, и он отлично работает:
public delegate void SetEGIDWithoutBoxingActionCast<T>(ref T target, EGID egid) where T : struct, IEntityComponent;
static class SetEGIDWithoutBoxing<T> where T : struct, IEntityComponent
{
public static readonly SetEGIDWithoutBoxingActionCast<T> SetIDWithoutBoxing = MakeSetter();
public static void Warmup() { }
static SetEGIDWithoutBoxingActionCast<T> MakeSetter()
{
if (ComponentBuilder<T>.HAS_EGID)
{
var method = typeof(Trick).GetMethod(nameof(Trick.SetEGIDImpl)).MakeGenericMethod(typeof(T));
return (SetEGIDWithoutBoxingActionCast<T>) Delegate.CreateDelegate(
typeof(SetEGIDWithoutBoxingActionCast<T>), method);
}
return null;
}
static class Trick
{
public static void SetEGIDImpl<U>(ref U target, EGID egid) where U : struct, INeedEGID
{
target.ID = egid;
}
}
}