Reputation: 29579
Given a function declaration
dynamic DoSomething(dynamic inputObject)
I can call it with an enum as inputObject
:
MyEnum myEnum;
myEnum = DoSomething(myEnum);
But for some reason if the function were to declare inputObject
as type ref dynamic
instead of dynamic
:
dynamic DoSomething(ref dynamic inputObject)
The following does not work due to invalid conversion:
MyEnum myEnum;
DoSomething(ref myEnum);
Is there something special about Enums that prevents me from using them with ref dynamic
?
Upvotes: 3
Views: 1699
Reputation: 2952
The only way to pass as a reference is convert the myEnum in a dynamic type and then pass it by reference. I think we should give a closer look at the generated IL to understand what's going on under the hood. Let's discover why and analyze this program:
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;
}
First we call DoSomethingByEnum passing the variable myEnum by value and then the DoSomethingDynamicByValue passing again myEnum but implicitly boxed as a dynamic type. This is what happens at MSIL level:
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
The only difference between DoSomethingByEnum(MyEnum) and DoSomethingDynamicByValue(dynamic) is boxing the variable myEnum (accomplished by creating a new object and copying the data from the value type into the newly allocated dynamic object). Check this out Box Opcode
Let's have a look to the DoSomethingByEnum(MyEnum) and 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
The IL code for both functions is the exact same, regardless of the type of the variable. We could even have a whatever object type but the way the variable is passed and shared across the calls woudln't change.
Let's see instead what happens in the DoSomethingDynamicByRef(ref dynamic).
To continue the main method
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
The difference between this IL and the last two previous examples relies on these two instructions to load and to fetch an address:
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
I think the reasons why it is not possible pass an address of a different object type is explained in the MSDN pages of the two IL instructions above ldloca.s and 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
hope this can clarify a bit.
Upvotes: 3
Reputation: 5023
dynamic
is actually object
after compiling, so you're actually asking why you cannot:
void DoSomething(ref object input);
MyEnum myEnum;
DoSomething(ref myEnum);
The reason is, ref
cannot be used like this, please consider how the case below violate the type safety:
void DoSomething(ref object input) {
input = new object();
}
So as Eric mentioned in the comment, Enum
is nothing special here.
Upvotes: 1