给定一个函数声明
dynamic DoSomething(dynamic inputObject)我可以使用枚举将其调用为inputObject
MyEnum myEnum;
myEnum = DoSomething(myEnum);但是由于某种原因,如果函数将inputObject声明为ref dynamic类型而不是dynamic类型
dynamic DoSomething(ref dynamic inputObject)以下内容由于无效转换而不起作用:
MyEnum myEnum;
DoSomething(ref myEnum);枚举是否有什么特殊之处,使我无法在ref dynamic中使用它们
发布于 2013-03-24 21:19:24
作为引用传递的唯一方法是将myEnum转换为动态类型,然后按引用传递它。我认为我们应该仔细研究一下生成的IL,以了解幕后发生了什么。让我们找出原因并分析这个程序:
enum MyEnum{
A,B
}
void Main()
{
MyEnum myEnum = MyEnum.B; //Assign a variable
DoSomethingByEnum(myEnum); //Pass myEnum
DoSomethingDynamicByValue(myEnum); //pass myEnum to a dynamic parameter
dynamic dyn = myEnum; //assign myenum to a dynamic variable
DoSomethingDynamicByRef(ref dyn); //pass it as a reference
}
MyEnum DoSomethingByEnum(MyEnum a)
{
return a;
}
dynamic DoSomethingDynamicByValue(dynamic inputObject)
{
return inputObject;
}
dynamic DoSomethingDynamicByRef(ref dynamic inputObject)
{
return inputObject;
} 首先,我们调用DoSomethingByEnum,通过值传递变量myEnum,然后调用DoSomethingDynamicByValue,再次传递myEnum,但隐式地将其包装为动态类型。这是在MSIL级别上发生的事情:
Main:
IL_0001: ldc.i4.1 // MyEnum myEnum = MyEnum.B;
IL_0002: stloc.0 // myEnum popped from evaluation stack and stored in a local variable
IL_0003: ldarg.0
IL_0004: ldloc.0 // myEnum loaded from local variable at index 0 and passed to the function
IL_0005: call DoSomethingByEnum
IL_000A: pop
IL_000B: ldarg.0
IL_000C: ldloc.0 // myEnum
IL_000D: box MyEnum // dynamic dyn = myEnum;
// myEnum Converted from value type to a true object reference of type dynamic
IL_0012: call DoSomethingDynamicByValueDoSomethingByEnum( myEnum )和DoSomethingDynamicByValue(动态)之间的唯一区别是对变量myEnum进行装箱(通过创建一个新对象并将数据从值类型复制到新分配的动态对象中来实现)。请查看此Box Opcode
让我们来看看DoSomethingByEnum(MyEnum)和DoSomethingDynamicByValue(动态) IL:
DoSomethingDynamicByValue:
IL_0000: nop
IL_0001: ldarg.1
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ldloc.0
IL_0006: ret
DoSomethingByEnum:
IL_0000: nop
IL_0001: ldarg.1
IL_0002: stloc.0
IL_0003: br.s IL_0005
IL_0005: ldloc.0
IL_0006: ret 无论变量的类型如何,两个函数的IL代码都是完全相同的。我们甚至可以有一个随心所欲的对象类型,但是通过调用传递和共享变量的方式是不会改变的。
让我们来看看在DoSomethingDynamicByRef中发生了什么(动态引用)。
继续使用main方法
Main:
IL_0018: ldloc.0 // myEnum
IL_0019: box UserQuery.MyEnum
IL_001E: stloc.1 // dyn
IL_001F: ldarg.0
IL_0020: ldloca.s 01 // loads the address of dyn onto the stack
IL_0022: call UserQuery.DoSomethingDynamicByRef
DoSomethingDynamicByRef:
IL_0000: nop
IL_0001: ldarg.1
IL_0002: ldind.ref //
IL_0003: stloc.0
IL_0004: br.s IL_0006
IL_0006: ldloc.0
IL_0007: ret此IL与前两个示例之间的差异依赖于加载和获取地址的这两条指令:
ldloca.s 01 // loads the address of dyn onto the stack
ldind.ref // Loads the object reference at address addr onto the stack as a type O我认为不可能传递不同对象类型的地址的原因在上面的ldloca.s和ldind.ref的两个IL指令的MSDN页面中解释了
格式正确的微软中间语言(MSIL)确保以与指针类型一致的方式使用ldind指令。最初推送到堆栈上的地址必须与计算机上对象的自然大小对齐
希望这能澄清一点。
发布于 2013-03-24 21:22:37
dynamic在编译后实际上是object,所以你实际上是在问为什么你不能:
void DoSomething(ref object input);
MyEnum myEnum;
DoSomething(ref myEnum);原因是,ref不能这样使用,请考虑下面的情况如何违反类型安全:
void DoSomething(ref object input) {
input = new object();
}因此,正如Eric在评论中提到的,Enum在这里并没有什么特别之处。
https://stackoverflow.com/questions/15582121
复制相似问题