幕后的Ref和OUT

时间:2018-04-10 08:20:53

标签: c# .net

我想知道ref和out关键字在幕后的工作原理吗? 例如,如果我们在方法上使用它,它会将此值类型变量放入某个类中,以便与引用类型一起使用吗?

2 个答案:

答案 0 :(得分:2)

  

为了与引用类型一起使用它?

首先,您需要了解“通过引用”(ref / out和现在in与“引用类型”不一样

最终,当你看到:

void SomeMethod(ref int x);

这里x是一个引用,也就是一个值的“托管指针”;呼叫者提供参考。所以,当你有:

int a = 123;
SomeMethod(ref a);

这可以通过获取变量a的地址(通常通过ldlocaldflda IL指令),并将地址作为参数值。

如果SomeMethod的正文是:

void SomeMethod(ref int x) {
    Write(x);
    x = 12;
}

然后Write(x) 取消引用指针x - 它会加载x的值,查看它指向的位置,并获取实际值从那里。同样,x=12获取值12,查看指针x并将12写入,指针指向

使用C#指针(又名“非托管指针”)在unsafe C#中编写等效内容以显示差异:

void SomeMethod(int* x) {
    Write(*x);
    *x = 12;
}
int a = 123;
SomeMethod(&a);

out只是ref的精美版本,具有某些“明确赋值”约定; in同样是ref的精美版本,允许readonly类型在避免堆栈副本的同时可靠有效地工作。

答案 1 :(得分:1)

对于ref,您可以查看为以下代码生成的IL代码:

public static void Main(string[] args)
{
    int x = 5;
    A(ref x);
}

private static void A(ref int x)
{
    x = 10;
}

你会看到以下内容:

.method public hidebysig static void  Main(string[] args) cil managed
{
    . . .
    IL_0001:  ldc.i4.5     //Push 5 onto the stack as int32.
    IL_0002:  stloc.0      //Pop a value from stack into local variable 0.
    IL_0003:  ldloca.s   x //Load address of local variable with index indx, short form.
    IL_0005:  call       void ConsoleApp1.Program::A(int32&) //Calls method A() by passing x's address to it
    . . . 
}

和方法A():

.method private hidebysig static void  A(int32& x) cil managed //x's address as argument
{
    // Code size       6 (0x6)
   .maxstack  8
   IL_0000:  nop
   IL_0001:  ldarg.0 //Load argument 0 onto the stack.
   IL_0002:  ldc.i4.s   10 //Push 10 onto the stack as int32, short form.
   IL_0004:  stind.i4 //Store value of type int32 into memory at address
   IL_0005:  ret
}

所以基本上它将x的地址传递给方法A(),其中10的值将被写入具有给定地址的内存中。

最后是out

public static void Main(string[] args)
{
    A(out int x);
}

private static void A(out int x)
{
    x = 10;
}

IL代码将是:

.method private hidebysig static void  A([out] int32& x) cil managed
{
    . . .
    IL_0001:  ldloca.s   x //Load address of local variable with index indx, short form.
    IL_0003:  call       void ConsoleApp1.Program::A(int32&) //Calls method A() by passing x's address to it
    . . .
}

和方法A():

.method private hidebysig static void  A([out] int32& x) cil managed
{
  // Code size       6 (0x6)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.0
  IL_0002:  ldc.i4.s   10
  IL_0004:  stind.i4
  IL_0005:  ret
} // end of method Program::A

正如您所看到的那样A(ref int x)A(out int x)方法只有[out]方法参数属性的区别,它将指示out传递的参数(不是ref })。