如何使用IL改变一个装箱结构假设我们有一个可变结构(是的,不要开始):publicstructMutableStruct{publicintFoo{get;放;}publicoverridestringToString(){returnFoo.ToString();}}使用反射,我们可以获得这个结构的盒装实例并在盒内改变它://这基本上就是我们想要模拟的对象obj=newMutableStruct{Foo=123};obj.GetType().GetProperty("Foo").SetValue(obj,456);System.Console.WriteLine(obj);//"456"我想做的是写一些可以在IL中执行的东西——但速度更快。我是元编程迷;取消设置p的任一值并使用常规IL更改值是微不足道的——但你不能事后调用它,因为那样会创建一个不同的框。我想我们需要做的是将它复制到现有的盒子上。我查看了ldobj/stobj,但这些似乎没有用(除非我遗漏了什么)。那么:有没有一种机制可以做到这一点?或者我是否必须限制自己进行反射以执行装箱结构的就地更新?或者换句话说:这里有什么……邪恶……?varmethod=newDynamicMethod("evil",null,new[]{typeof(object),typeof(object)});varil=方法。获取ILGenerator();//...邪恶在这里...il。发射(OpCodes.Ret);动作action=(Action)method.CreateDelegate(typeof(Action));行动(目标,789);System.Console.WriteLine(obj);//"789"嗯,这很有趣。使用Ldflda和Stind_*似乎有效。实际上,它主要是Unbox(请参阅使用Ldflda和Stind_*的版本历史)。这就是我在LinqPad中一起破解来证明的。publicstructMutableStruct{publicintFoo{get;放;}publicoverridestringToString(){returnFoo.ToString();}}voidMain(){varfoo=typeof(MutableStruct).GetProperty("Foo");varsetFoo=foo.SetMethod;vardynMtd=newDynamicMethod("Evil",typeof(void),new[]{typeof(object),typeof(int)});varil=dynMtd.GetILGenerator();il.Emit(OpCodes.Ldarg_0);//对象il.Emit(OpCodes.Unbox,typeof(MutableStruct));//MutableStruct&il.Emit(OpCodes.Ldarg_1);//MutableStruct&intil.Emit(OpCodes.Call,setFoo);//--empty--il.Emit(OpCodes.Ret);//--empty--vardel=(Action)dynMtd.CreateDelegate(typeof(Action));varmut=newMutableStruct{Foo=123};varboxed=(对象)mut;删除(盒装,456);varunboxed=(MutableStruct)boxed;//unboxed.Foo=456,mut.Foo=123}干得好:只是使用不安全?staticvoidMain(string[]args){objectfoo=newMutableStruct{Foo=123};控制台.WriteLine(foo);酒吧(富);控制台.WriteLine(foo);}英石aticunsafevoidBar(objectfoo){GCHandleh=GCHandle.Alloc(foo,GCHandleType.Pinned);MutableStruct*fp=(MutableStruct*)(void*)h.AddrOfPinnedObject();fp->Foo=789;}IL实现留作读者练习更新:根据Kevin的回答,这里有一个最小的工作示例:ldarg.0unboxMutableStructldarg.1callinstancevoidMutableStruct::set_Foo(int32)ret您可以更轻松地完成此操作。在我们有动态的.NET4.5下试试这个。结构测试{publicInt32Number{get;放;}publicoverridestringToString(){returnthis.Number.ToString();}}classProgram{staticvoidMain(string[]args){Objecttest=newTest();动态代理=测试;proxy.Number=1;控制台.WriteLine(测试);控制台.ReadLine();我知道这不是反射,但它仍然很有趣。即使没有不安全的代码,纯C#:usingSystem;内部接口I{voidIncrement();}structS:I{publicreadonlyintValue;publicS(intvalue){值=值;}publicvoidIncrement(){this=newS(Value+1);//纯粹的邪恶:O}publicoverridestringToString(){returnValue.ToString();}}classProgram{staticvoidMain(){objects=newS(123);((I)s).增量();控制台.WriteLine(s);//打印124}}在C#中,值类型实例方法中的这个引用实际上是一个ref参数(或构造函数中的值类型out参数,这就是为什么不能像ref/out参数一样将其捕获到闭包中的原因在任何方法中)并且可以修改。在未装箱的值上调用结构实例方法时,this赋值有效地替换了调用站点的值。当在装箱实例上调用实例方法时(通过上例中的虚拟调用或接口调用),ref参数指向装箱对象内部的值,因此可以修改装箱值。我已经发布了一个使用表达式树在另一个线程中设置字段的解决方案。使用属性更改代码是微不足道的:以上是C#学习教程:如何使用IL更改装箱结构的全部内容。网络收藏不代表立场,如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
