Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/319.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-mvc/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何在C#/IL中变异装箱值类型(原语或结构)_C#_.net_Cil_Reflection.emit - Fatal编程技术网

如何在C#/IL中变异装箱值类型(原语或结构)

如何在C#/IL中变异装箱值类型(原语或结构),c#,.net,cil,reflection.emit,C#,.net,Cil,Reflection.emit,与之相关,我正在尝试更改装箱值类型的值,但是以通用方式,因此尝试实现以下方法: void MutateValueType<T>(object o, T v) where T : struct 以下是我提出的实施方案: public static void MutateValueType<T>(object o, T v) { var dynMtd = new DynamicMethod("EvilMutateValueType", typeof

与之相关,我正在尝试更改装箱值类型的值,但是以通用方式,因此尝试实现以下方法:

void MutateValueType<T>(object o, T v) where T : struct
以下是我提出的实施方案:

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);
}
publicstaticvoid MutateValueType(对象o,tv)
{
var dynMtd=新的DynamicMethod(“EvillumateValueType”,
typeof(void),新类型[]{typeof(object),typeof(T)};
var il=dynMtd.GetILGenerator();
il.Emit(操作码.Ldarg_0);//对象
il.Emit(opcode.Unbox,typeof(T));//T&
il.Emit(操作码.Ldarg_1);//T(参数值)
Emit(opcode.Stobj,typeof(T));//Stobj!!T
发射(操作码Ret);
var del=(Action)dynMtd.CreateDelegate(typeof(Action));
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
  }
.method public hidebysing static void Mutate(对象o,!!T值)cil管理的攻击性内联
{
.maxstack 2
ldarg.0
拆箱!!T
ldarg.1
stobj!!T
ret
}

一种解决方案是在IL中创建一个
Unbox
方法,该方法调用
Unbox
,并将
ref
返回到对象中包含的类型:

.method public hidebysig static !!T&  Unbox<T>(object o) cil managed aggressiveinlining
{
  .maxstack  1
  ldarg.0
  unbox !!T
  ret
}
.method公共隐藏静态!!T&Unbox(对象o)cil管理的攻击性联机
{
.maxstack 1
ldarg.0
拆箱!!T
ret
}
然后像这样使用:

public static void MutateValueType<T>(object o, T v)
{
    ref T ub = ref Unsafe.Unbox<T>(o);
    ub = v;
}
publicstaticvoid MutateValueType(对象o,tv)
{
ref T ub=ref Unbox(o);
ub=v;
}
这将正确地输出:

        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
var oi=(object)17;
突变型(oi,43);
控制台写入线(oi);//43
var od=(对象)17.7d;
变异价值型(od,42.3);
控制台写入线(od);//42.3
注意:这需要C#7支持
ref
返回


这可能是为了说,但是它也必须可以与
il.Emit
一起使用,所以我正在寻找它。

不同的是
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受控可变性管理指针

readonly.
前缀和
unbox
指令可以生成所谓的受控可变管理指针。与普通托管指针类型不同,受控可变托管指针不是可分配给(§III.1.8.1.2.3)普通托管指针的验证器;e、 不可能 作为byref参数传递给方法。在控制流点,受控可变性托管指针可以与相同类型的托管指针合并,以生成受控可变性托管指针

受控可变性托管指针只能以以下方式使用:

  • 作为
    ldfld
    ldflda
    stfld
    call
    callvirt
    约束的对象参数。callvirt
    指令
  • 作为指向
    ldind.*
    ldobj
    指令的指针参数
  • 作为
    cpobj
    指令的源参数
  • 所有其他操作(包括
    stobj
    stind.*
    initobj
    mkrefany
    )均无效

    [……]

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

    III.4.29 stobj–将值存储在地址上

    [……]

    正确性:

    正确的CIL确保dest是指向
    T
    的指针,并且src的类型是可分配给
    T
    的验证器

    [……]

    注意,这里对受控可变性托管指针没有限制,允许任何指向
    T
    的指针


    因此,确保不验证IL是正确的方法。

    我不确定是否可以完全替换框中的内容,也不确定为什么您认为应该这样做(至少是以通用方式)。因为
    unbox
    只是返回一个指向该值的托管指针,或者换句话说,包含值类型的内存地址。应该可以为此分配一个新值。这应该对应于一个返回<代码> REF <代码>的方法,例如<代码> REF unbox(object o)< /> >,事实上,我认为这可以在IL实现,然后用于上述。@ DaMiNeNeth-Tun-Unistiver考虑了一个简单的<代码>结构>邪恶{int i;公共覆盖字符串toString(){{+i;返回i toString();}}//>然后
    objecto=newevil()。现在,规范要求
    o.ToString()!=o、 ToString()
    ——也就是说,规范要求运行时支持装箱值的就地更新。鉴于无论如何都需要运行时来支持这一点,并且鉴于我的示例没有使用任何实际需要相关值类型合作的内容,我似乎很清楚OP后面的内容应该是可能的。@hvd-规范要求结构的成员是可更新的。它不要求整个结构都是可重放的。也就是说,它不要求
    struct
    构造函数中可能的
    this
    赋值一般可用。@Damien_不相信我的例子,我可以很容易地把
    this=new-Evil{I=I+1}
    修改整个结构。结构赋值从未局限于构造函数。这很有效。非常感谢你。如果你愿意的话,也许你可以在最后添加一个注释,并附上最终的完整代码
            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
    
    var dynMtd = new DynamicMethod("EvilMutateValueType",
        typeof(void), new Type[] { typeof(object), typeof(T) }, typeof(Program).Module);
    // Use whatever class you have available here.              ^^^^^^^^^^^^^^^^^^^^^^