3

関数宣言が与えられた場合

dynamic DoSomething(dynamic inputObject)

次のように列挙型で呼び出すことができますinputObject

MyEnum myEnum;
myEnum = DoSomething(myEnum);

しかし、何らかの理由で、関数が の代わりにinputObject型として宣言された場合:ref dynamicdynamic

dynamic DoSomething(ref dynamic inputObject)

変換が無効なため、以下は機能しません。

MyEnum myEnum;
DoSomething(ref myEnum);

Enums で使用できない特別なことがありref dynamicますか?

4

2 に答える 2

3

参照として渡す唯一の方法は、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;

} 

最初に変数 myEnum を値で渡して DoSomethingByEnum を呼び出し、次に再度 myEnum を渡す DoSomethingDynamicByValue を呼び出しますが、動的型として暗黙的にボックス化されます。これは、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:
    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 と前の 2 つの例の違いは、次の 2 つの命令に依存してアドレスをロードおよびフェッチすることです。

   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.sldind.refの上の 2 つの IL 命令の MSDN ページで説明されていると思います。

正しい形式の Microsoft Intermediate Language (MSIL) により、ポインタの型と一貫した方法で ldind 命令が使用されます。スタックに最初にプッシュされたアドレスは、マシン上のオブジェクトの自然なサイズに合わせる必要があります

これが少し明確になることを願っています。

于 2013-03-24T13:19:24.050 に答える
1

dynamic実際にはobjectコンパイル後なので、実際にできない理由を尋ねています:

void DoSomething(ref object input);

MyEnum myEnum;
DoSomething(ref myEnum);

理由は、refこのように使用できないためです。以下のケースがどのようにタイプ セーフに違反しているかを検討してください。

void DoSomething(ref object input) {
    input = new object();
}

エリックがコメントで述べたように、Enumここでは特別なことは何もありません。

于 2013-03-24T13:22:36.520 に答える