给定一个函数声明
dynamic DoSomething(dynamic inputObject)
我可以用枚举调用它作为inputObject
:
MyEnum myEnum;
myEnum = DoSomething(myEnum);
但出于某种原因,如果函数将 inputObject
声明为类型 ref dynamic
thay vì dynamic
:
dynamic DoSomething(ref dynamic inputObject)
由于转换无效,以下操作无效:
MyEnum myEnum;
DoSomething(ref myEnum);
枚举是否有什么特别之处阻止我将它们与 ref dynamic
一起使用?
作为引用传递的唯一方法是将 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 DoSomethingDynamicByValue
DoSomethingByEnum(MyEnum) 和 DoSomethingDynamicByValue(dynamic) 之间的唯一区别是装箱变量 myEnum(通过创建一个新对象并将数据从值类型复制到新分配的动态对象中来实现)。检查一下 Box Opcode
让我们看看 DoSomethingByEnum(MyEnum) 和 DoSomethingDynamicByValue(dynamic) 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(ref dynamic) 中发生了什么。
继续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
我认为不可能传递不同对象类型的地址的原因在上面两条 IL 指令的 MSDN 页面中有解释 ldloca.sVà ldind.ref
Correctly-formed Microsoft Intermediate Language (MSIL) ensures that the ldind instructions are used in a manner consistent with the type of the pointer. The address initially pushed onto the stack must be aligned to the natural size of objects on the machine
希望这可以澄清一点。
Tôi là một lập trình viên xuất sắc, rất giỏi!