C# 将 T where T: struct 转换为不装箱的接口
C# Casting T where T: struct to an interface without boxing
我需要编写一个免分配代码,在从具有结构约束的通用参数 T 转换为接口以访问已实现的接口时避免装箱 属性。
我之前使用动态生成的代码和 lambda 表达式树解决了这个问题,例如:
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;
}
生成的 "setter" 委托将允许将 属性 值设置为实现 ID 属性 的任何结构,无需任何装箱。
但是现在我需要在不生成动态代码的情况下解决同样的问题。我做了一些快速实验,但似乎 Unsafe class 无法使用任何 As 方法,可能是因为结构不能直接转换为接口,因为它被视为对象,因此不能用指针直接处理。
任何指针(没有双关语意)?
编辑:我不一定需要将结构转换为接口,我只需要能够在 属性 ID 中写入值即可。它是 属性 而不是字段这一事实并不能确定(因为我发现一些代码能够计算结构中字段的偏移量,但不能用于 属性 当然)
答案被接受,最终代码是这样的,它工作正常:
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;
}
}
}
您不能在不装箱的情况下将结构转换为接口。接口调用通过在 object 的 header 中找到的 vtable 工作,未装箱的结构没有。
但是,我相信您可以通过使用 Delegate.CreateDelegate
.
为受约束的泛型方法创建不受约束的委托来完成您想要做的事情
// No constraints needed on the delegate type
public delegate void SetEGIDWithoutBoxingActionCast<T>(ref T target, EGID egid);
public static SetEGIDWithoutBoxingActionCast<T> MakeSetter<T>()
{
var method = typeof(Foo).GetMethod(nameof(Foo.SetEGIDImpl)).MakeGenericMethod(typeof(T));
return (SetEGIDWithoutBoxingActionCast<T>)Delegate.CreateDelegate(typeof(SetEGIDWithoutBoxingActionCast<T>), method);
}
public static class Foo
{
public static void SetEGIDImpl<T>(ref T target, EGID egid) where T : INeedEGID
{
target.ID = egid;
}
}
我需要编写一个免分配代码,在从具有结构约束的通用参数 T 转换为接口以访问已实现的接口时避免装箱 属性。
我之前使用动态生成的代码和 lambda 表达式树解决了这个问题,例如:
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;
}
生成的 "setter" 委托将允许将 属性 值设置为实现 ID 属性 的任何结构,无需任何装箱。
但是现在我需要在不生成动态代码的情况下解决同样的问题。我做了一些快速实验,但似乎 Unsafe class 无法使用任何 As 方法,可能是因为结构不能直接转换为接口,因为它被视为对象,因此不能用指针直接处理。
任何指针(没有双关语意)?
编辑:我不一定需要将结构转换为接口,我只需要能够在 属性 ID 中写入值即可。它是 属性 而不是字段这一事实并不能确定(因为我发现一些代码能够计算结构中字段的偏移量,但不能用于 属性 当然)
答案被接受,最终代码是这样的,它工作正常:
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;
}
}
}
您不能在不装箱的情况下将结构转换为接口。接口调用通过在 object 的 header 中找到的 vtable 工作,未装箱的结构没有。
但是,我相信您可以通过使用 Delegate.CreateDelegate
.
// No constraints needed on the delegate type
public delegate void SetEGIDWithoutBoxingActionCast<T>(ref T target, EGID egid);
public static SetEGIDWithoutBoxingActionCast<T> MakeSetter<T>()
{
var method = typeof(Foo).GetMethod(nameof(Foo.SetEGIDImpl)).MakeGenericMethod(typeof(T));
return (SetEGIDWithoutBoxingActionCast<T>)Delegate.CreateDelegate(typeof(SetEGIDWithoutBoxingActionCast<T>), method);
}
public static class Foo
{
public static void SetEGIDImpl<T>(ref T target, EGID egid) where T : INeedEGID
{
target.ID = egid;
}
}