假设我有以下代码更新 struct
的字段使用反射。由于结构实例被复制到 DynamicUpdate
方法,it needs to be boxed to an object before being passed .
struct Person
{
public int id;
}
class Test
{
static void Main()
{
object person = RuntimeHelpers.GetObjectValue(new Person());
DynamicUpdate(person);
Console.WriteLine(((Person)person).id); // print 10
}
private static void DynamicUpdate(object o)
{
FieldInfo field = typeof(Person).GetField("id");
field.SetValue(o, 10);
}
}
代码运行良好。现在,假设我不想使用反射,因为它很慢。相反,我想生成一些 CIL 直接修改 id
字段并将该 CIL 转换为可重用委托(delegate)(例如,使用动态方法功能)。特别地,我想用这样的 s/t 替换上面的代码:
static void Main()
{
var action = CreateSetIdDelegate(typeof(Person));
object person = RuntimeHelpers.GetObjectValue(new Person());
action(person, 10);
Console.WriteLine(((Person)person).id); // print 10
}
private static Action<object, object> CreateSetIdDelegate(Type t)
{
// build dynamic method and return delegate
}
我的问题:有没有办法实现CreateSetIdDelegate
除了使用以下技术之一?
- 生成使用反射调用 setter 的 CIL(作为本文中的第一个代码段)。这是没有意义的,因为要求是摆脱反射,但这是一个可能的实现,所以我只是提到。
- 而不是使用
Action<object, object>
, 使用签名为public delegate void Setter(ref object target, object value)
的自定义委托(delegate). - 而不是使用
Action<object, object>
, 使用Action<object[], object>
数组的第一个元素是目标对象。
我不喜欢 2 和 3 的原因是因为我不想为对象的 setter 和 struct 的 setter 使用不同的委托(delegate)(也不想让 set-object-field 委托(delegate)更复杂不必要的,例如 Action<object, object>
)。我估计 CreateSetIdDelegate
的执行会根据目标类型是结构还是对象生成不同的 CIL,但我希望它返回向用户提供相同 API 的相同委托(delegate)。
最佳答案
再次编辑:现在结构有效。
在 C# 4 中有一种绝妙的方法可以做到这一点,但在此之前您必须编写自己的 ILGenerator
发出代码。他们向 .NET Framework 4 添加了 ExpressionType.Assign
。
这适用于 C# 4(已测试):
public delegate void ByRefStructAction(ref SomeType instance, object value);
private static ByRefStructAction BuildSetter(FieldInfo field)
{
ParameterExpression instance = Expression.Parameter(typeof(SomeType).MakeByRefType(), "instance");
ParameterExpression value = Expression.Parameter(typeof(object), "value");
Expression<ByRefStructAction> expr =
Expression.Lambda<ByRefStructAction>(
Expression.Assign(
Expression.Field(instance, field),
Expression.Convert(value, field.FieldType)),
instance,
value);
return expr.Compile();
}
编辑:这是我的测试代码。
public struct SomeType
{
public int member;
}
[TestMethod]
public void TestIL()
{
FieldInfo field = typeof(SomeType).GetField("member");
var setter = BuildSetter(field);
SomeType instance = new SomeType();
int value = 12;
setter(ref instance, value);
Assert.AreEqual(value, instance.member);
}
关于c# - 生成动态方法来设置结构的字段而不是使用反射,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1272454/