我想知道ref和out关键字在幕后的工作原理吗? 例如,如果我们在方法上使用它,它会将此值类型变量放入某个类中,以便与引用类型一起使用吗?
答案 0 :(得分:2)
为了与引用类型一起使用它?
首先,您需要了解“通过引用”(ref
/ out
和现在in
)与“引用类型”不一样 ”
最终,当你看到:
void SomeMethod(ref int x);
这里x
是一个引用,也就是一个值的“托管指针”;呼叫者提供参考。所以,当你有:
int a = 123;
SomeMethod(ref a);
这可以通过获取变量a
的地址(通常通过ldloca
或ldflda
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
})。