C# 反射 - 如何为结构设置字段值 [英] C# Reflection - How to set field value for struct

查看:20
本文介绍了C# 反射 - 如何为结构设置字段值的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

如何使用 DynamicMethod 通过反射将值设置到结构字段 - myStruct.myField 中?当我调用 setter(myStruct, 111) 时,值未设置,因为 MyStruct 是值类型.Console.WriteLine(myStruct.myField) 显示值 3.
如何修改GetDelegate方法来设置myStruct.myField的值?

How can I set value into struct field - myStruct.myField with reflection using DynamicMethod? When I call setter(myStruct, 111) value was not set, because MyStruct is value type. Console.WriteLine(myStruct.myField) shows value 3.
How to modify GetDelegate method to set value into myStruct.myField?

public struct MyStruct
{
    public int myField;
}

public delegate void SetHandler(object source, object value);

private static SetHandler GetDelegate(Type type, FieldInfo fieldInfo)
{
    DynamicMethod dm = new DynamicMethod("setter", typeof(void), new Type[] { typeof(object), typeof(object) }, type, true);
    ILGenerator setGenerator = dm.GetILGenerator();

    setGenerator.Emit(OpCodes.Ldarg_0);
    setGenerator.DeclareLocal(type);
    setGenerator.Emit(OpCodes.Unbox_Any, type);
    setGenerator.Emit(OpCodes.Stloc_0);
    setGenerator.Emit(OpCodes.Ldloca_S, 0);
    setGenerator.Emit(OpCodes.Ldarg_1);
    setGenerator.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
    setGenerator.Emit(OpCodes.Stfld, fieldInfo);
    setGenerator.Emit(OpCodes.Ldloc, 0);
    setGenerator.Emit(OpCodes.Box, type);
    setGenerator.Emit(OpCodes.Ret);
    return (SetHandler)dm.CreateDelegate(typeof(SetHandler));
}

MyStruct myStruct = new MyStruct();
myStruct.myField = 3;

FieldInfo fi = typeof(MyStruct).GetField("myField", BindingFlags.Public | BindingFlags.Instance);

SetHandler setter = GetDelegate(typeof(MyStruct), fi);
setter(myStruct, 111);
Console.WriteLine(myStruct.myField);

推荐答案

我又犯了这个错误 - 有趣的事实;unbox-any 返回;unbox 返回指向数据的指针 - 这允许就地变异.

I made this mistake again - fun fact; unbox-any returns the value; unbox returns the pointer to the data - which allows in-place mutate.

这是工作的 IL 生成:

Here's the working IL generation:

    setGenerator.Emit(OpCodes.Ldarg_0);
    setGenerator.Emit(OpCodes.Unbox, type);
    setGenerator.Emit(OpCodes.Ldarg_1);
    setGenerator.Emit(OpCodes.Unbox_Any, fieldInfo.FieldType);
    setGenerator.Emit(OpCodes.Stfld, fieldInfo);
    setGenerator.Emit(OpCodes.Ret);

但是!这是对盒装副本进行变异;之后您需要拆箱:

But! This is mutating a boxed copy; you would need to unbox afterwards:

    object obj = myStruct;
    setter(obj, 111);
    MyStruct andBackAgain = (MyStruct)obj;
    Console.WriteLine(andBackAgain.myField);
    Console.WriteLine(myStruct.myField);

要在适当的位置执行此操作,您可能需要使用 ref MyStructreturn 一个 MyStruct 的方法.您可以退回装箱的副本,但这并不会使其更易于使用.坦率地说,这是没有实际意义的:结构通常不应该是可变的.

To do it in place, you would probably need the method to take a ref MyStruct, or to return a MyStruct. You could return the boxed copy, but that doesn't make it much easier to use. Frankly, it is moot: structs should not generally be mutable.

这篇关于C# 反射 - 如何为结构设置字段值的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆