c# - 如何在 C#/IL 中改变盒装值类型(原始或结构)

标签 c# .net cil reflection.emit

How to mutate a boxed struct using IL 相关我正在尝试以通用方式更 retrofit 箱值类型的值,因此尝试实现以下方法:

void MutateValueType<T>(object o, T v) where T : struct

所以以下应该是可能的:

var oi = (object)17;
MutateValueType<int>(oi, 43);
Console.WriteLine(oi); // 43

var od = (object)17.7d;
MutateValueType<double>(od, 42.3);
Console.WriteLine(od); // 42.3

我无法让它在 .NET Framework 上运行(请参阅 @hvd 的评论,即没有 typeof(Program).Module 的实现适用于其他运行时)。我已经实现了这一点,如下所示。但是,当调用委托(delegate) del 时,这会失败:

System.Security.VerificationException: 'Operation could destabilize the runtime.'

这是我想出的实现:

public static void MutateValueType<T>(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<object, T>)dynMtd.CreateDelegate(typeof(Action<object, T>));
    del(o, v);
}

上面应该等效于下面的IL,可以工作,但上面仍然失败,所以问题是为什么这不起作用。

  .method public hidebysig static void Mutate<T>(object o, !!T Value) cil managed aggressiveinlining
  {
    .maxstack  2
    ldarg.0
    unbox !!T
    ldarg.1
    stobj !!T
    ret
  }

最佳答案

不同的是,DynamicMethod默认需要可验证代码,而你自己的代码(包括自定义IL)默认允许不可验证。

您可以将 DynamicMethod 视为您自己的模块的一部分,通过指定模块来允许它包含无法验证的 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 中的一些其他问题导致难以获得良好的诊断,但看起来这至少是不可验证的:

III.1.8.1.2.2 Controlled-mutability managed pointers

The readonly. prefix and unbox instructions can produce what is called a controlled-mutability managed pointer. Unlike ordinary managed pointer types, a controlled-mutability managed pointer is not verifier-assignable-to (§III.1.8.1.2.3) ordinary managed pointers; e.g., it cannot be passed as a byref argument to a method. At control flow points, a controlled-mutability managed pointer can be merged with a managed pointer of the same type to yield a controlled-mutability managed pointer.

Controlled-mutability managed pointers can only be used in the following ways:

  1. As the object parameter for an ldfld, ldflda, stfld, call, callvirt, or constrained. callvirt instruction.
  2. As the pointer parameter to a ldind.* or ldobj instruction.
  3. As the source parameter to a cpobj instruction.

All other operations (including stobj, stind.*, initobj, and mkrefany) are invalid.

[...]

但看起来它仍然是正确的:

III.4.29 stobj – store a value at an address

[...]

Correctness:

Correct CIL ensures that dest is a pointer to T and the type of src is verifier-assignable-to T.

[...]

请注意,此处对受控可变性托管指针没有限制,任何指向 T 的指针都是允许的。

因此,确保不对您的 IL 进行验证是正确的方法。

关于c# - 如何在 C#/IL 中改变盒装值类型(原始或结构),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/44724042/

相关文章:

.net - 将 .NET 表达式树链接到新程序集

javascript - 出于开发目的,如何将 connectionStrings 中的数据库名称显示到我的网页上?

c# - 构造函数是否返回 null?

c# - 更改填充的 DataTable 列数据类型

.net - .NET ThreadPool 线程在返回池时是否会重置?

c# - 如何使用 CIL 将 POCO 转换为数组?

c# - 使用 Xamarin 连接到远程 MySQL

c# - .NET 2.0 中的 SignumFramework?

.net - .NET app.config 中的 Couchbase 多个存储桶

c# - 无参数构造函数的第一个参数是什么?