如何在C#/ IL中改变盒装值类型(原语或结构)
与如何使用IL改变盒装结构相关我试图以通用方式更改盒装值类型的值,因此尝试实现以下方法:
void MutateValueType(object o, T v) where T : struct
所以以下应该是可能的:
var oi = (object)17; MutateValueType(oi, 43); Console.WriteLine(oi); // 43 var od = (object)17.7d; MutateValueType(od, 42.3); Console.WriteLine(od); // 42.3
我没有得到它在.NET Framework上工作(请参阅@hvd的评论,没有typeof(Program).Module
适用于其他运行时)。 我已经实现了这个,如下所示。 但是,在使用以下命令调用委托del
时,这会失败:
System.Security.VerificationException: 'Operation could destabilize the runtime.'
这是我提出的实现:
public static void MutateValueType(object o, T v) { var dynMtd = new DynamicMethod("EvilMutateValueType", typeof(void), new Type[] { typeof(object), typeof(T) }); var il = dynMtd.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); // object il.Emit(OpCodes.Unbox, typeof(T)); // T& il.Emit(OpCodes.Ldarg_1); // T (argument value) il.Emit(OpCodes.Stobj, typeof(T)); // stobj !!T il.Emit(OpCodes.Ret); var del = (Action)dynMtd.CreateDelegate(typeof(Action)); del(o, v); }
以上应该等同于下面的IL,这有效,但仍然上面的失败,所以问题是为什么这不起作用。
.method public hidebysig static void Mutate(object o, !!T Value) cil managed aggressiveinlining { .maxstack 2 ldarg.0 unbox !!T ldarg.1 stobj !!T ret }
不同之处在于,默认情况下DynamicMethod
需要可validation的代码,而默认情况下,您自己的代码(包括自定义IL)是无法validation的。
您可以将DynamicMethod
视为您自己模块的一部分,通过指定模块允许它包含无法validation的IL:
var dynMtd = new DynamicMethod("EvilMutateValueType", typeof(void), new Type[] { typeof(object), typeof(T) }, typeof(Program).Module); // Use whatever class you have available here. ^^^^^^^^^^^^^^^^^^^^^^
尽管PEVerify中的一些其他问题使得很难获得良好的诊断,但看起来这至少是不可validation的:
III.1.8.1.2.2受控可变性管理指针
readonly.
前缀和unbox
指令可以产生所谓的受控可变性管理指针 。 与普通托管指针类型不同,受控可变性管理指针不是validation者可分配给 (§III.1.8.1.2.3)普通托管指针; 例如,它不能作为byref参数传递给方法。 在控制流点处,受控可变性管理指针可以与相同类型的受管指针合并,以产生受控可变性管理指针。受控可变性管理指针只能以下列方式使用:
- 作为
ldfld
,ldflda
,stfld
,call
,callvirt
或constrained. callvirt
的对象参数constrained. callvirt
constrained. callvirt
指令。- 作为
ldind.*
或ldobj
指令的指针参数。- 作为
cpobj
指令的源参数。所有其他操作(包括
stobj
,stind.*
,initobj
和mkrefany
)都无效。[…]
但看起来它仍然是正确的:
III.4.29 stobj – 将值存储在地址中
[…]
正确性:
正确的CIL确保dest是指向
T
的指针, src的类型是verifier-assignable-toT
[…]
请注意,此处对受控可变性管理指针没有限制,允许任何指向T
指针。
因此,确保您的IL无法进行validation是正确的方法。
一种解决方案是在IL中创建一个调用unbox
的Unbox
方法,并将ref
返回给对象中包含的类型:
.method public hidebysig static !!T& Unbox(object o) cil managed aggressiveinlining { .maxstack 1 ldarg.0 unbox !!T ret }
然后使用这个:
public static void MutateValueType(object o, T v) { ref T ub = ref Unsafe.Unbox (o); ub = v; }
这正确输出:
var oi = (object)17; MutateValueType(oi, 43); Console.WriteLine(oi); // 43 var od = (object)17.7d; MutateValueType(od, 42.3); Console.WriteLine(od); // 42.3
注意:这需要C#7支持ref
返回。
这可能会添加到https://github.com/DotNetCross/Memory.Unsafe,但也必须使用il.Emit
,所以我正在寻找。
上述就是C#学习教程:如何在C#/ IL中改变盒装值类型(原语或结构)分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注—计算机技术网(www.ctvol.com)!
本文来自网络收集,不代表计算机技术网立场,如涉及侵权请联系管理员删除。
ctvol管理联系方式QQ:251552304
本文章地址:https://www.ctvol.com/cdevelopment/1011259.html