C# 中的 ref 参数在堆栈上会发生什么?

发布于 2024-12-06 19:27:04 字数 134 浏览 0 评论 0原文

我正在阅读一些有关 WCF 和 IDispatchMessageInspector 的 C# 文档,该接口定义了一个通过引用传递的“消息”对象,以便可以对其进行操作。

当您通过 ref 传递某些内容而不是正常传递时,堆栈上实际发生了什么?

Im reading some C# documentation about WCF and IDispatchMessageInspector and the interface defines a 'Message' object that is passed by reference so that it can be manipulated.

What actually happens on the stack when you pass something by ref as opposed to passing normally?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

不如归去 2024-12-13 19:27:04

通过引用传递的不是对象,而是变量

基本上,它为调用方用作参数的变量以及您调用的方法中的参数别名:

public void Foo()
{
    int x = 10;
    Bar(ref x);
    Console.WriteLine(x); // Prints 20
}

public void Bar(ref int y)
{
    y = 20;
}

这里,x和< code>y 本质上是相同的变量 - 它们引用相同的存储位置。对 x 所做的更改可通过 y 看到,反之亦然。 (请注意,在本例中,它是调用者中的局部变量,但不一定是 - 如果您通过引用传递了实例变量,则 Bar可能会调用另一个方法来更改同一变量,然后 y 将被视为“神奇地”更改...)

有关 C# 中参数传递的更多信息,请参阅我的 有关该主题的文章

It's not the object that's passed by reference - it's the variable.

Basically it aliases the variable used as the argument from the calling side, and the parameter in the method that you call:

public void Foo()
{
    int x = 10;
    Bar(ref x);
    Console.WriteLine(x); // Prints 20
}

public void Bar(ref int y)
{
    y = 20;
}

Here, x and y are essentially the same variable - they refer to the same storage location. Changes made to x are visible via y and vice versa. (Note that in this case it's a local variable in the caller, but it doesn't have to be - if you'd passed an instance variable by reference, then Bar might call another method which changes the same variable, and then y would be seen to "magically" change...)

For more about parameter passing in C#, refer to my article on the subject.

围归者 2024-12-13 19:27:04

通过引用意味着您可以更改传递给项目的原始变量。它基本上传递堆栈上变量的地址而不是变量值。

IL 转储:

正如您实际询问堆栈上实际发生的情况,这里是按引用按值方法的 IL 转储:

.method private hidebysig instance void  ByRef(string& s) cil managed
{
  // Code size       9 (0x9)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldstr      "New value"
  IL_0007:  stind.ref
  IL_0008:  ret
} // end of method Class1::ByRef

.method private hidebysig instance void  ByValue(string s) cil managed
{
  // Code size       9 (0x9)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "New value"
  IL_0006:  starg.s    s
  IL_0008:  ret
} // end of method Class1::ByValue

正如您所看到的,主要区别在于参数类型(string& 而不是 string),并且它执行了额外的步骤来间接加载和存储值。

简单的C#源码如下所示,供参考:

void ByRef(ref string s)
{
    s = "New value";
}

void ByValue(string s)
{
    s = "New value";
}

By reference means you can change the original variable passed to the item. It basically passes the address of the variable on the stack rather than the variable value.

IL Dump:

As you actually asked what actually happens on the stack here is an IL dump of a by ref and by value method:

.method private hidebysig instance void  ByRef(string& s) cil managed
{
  // Code size       9 (0x9)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldarg.1
  IL_0002:  ldstr      "New value"
  IL_0007:  stind.ref
  IL_0008:  ret
} // end of method Class1::ByRef

vs.

.method private hidebysig instance void  ByValue(string s) cil managed
{
  // Code size       9 (0x9)
  .maxstack  8
  IL_0000:  nop
  IL_0001:  ldstr      "New value"
  IL_0006:  starg.s    s
  IL_0008:  ret
} // end of method Class1::ByValue

As you can see the main difference is the parameter type (string& instead of string) and that it does extra steps to load and store the values indirectly.

The simple C# source is shown below for reference:

void ByRef(ref string s)
{
    s = "New value";
}

void ByValue(string s)
{
    s = "New value";
}
独夜无伴 2024-12-13 19:27:04

当你按价值传递某物时;在调用函数之前,会创建值类型的副本并将其放入堆栈中。当您通过引用传递某些内容时,该地址将被压入堆栈而不是创建副本,并且当您修改函数中的对象时,将修改原始对象而不是副本。

它的工作方式是,当编译器发现参数是通过 ref 传递时,会将您对变量的引用转换为间接内存访问。

例如,让我们假设在内存位置 100 有一个整数 123;现在,当您调用一个按值接受的函数(默认情况下)时,将在调用该函数之前制作 123 的副本并将其推送到堆栈上,这意味着该副本现在将位于(可以说)160 地址。但是,当您通过引用传递某些内容时,地址 100 将被推入堆栈并驻留在位置 160 上。

现在生成的指令将读取 160 以获取对象的位置,然后修改 100 处的数据。间接发生的情况与原样相同当您使用 * 运算符时,完成指针。

When you pass something by value; a copy of the value type is made and put on stack before the function is called. When you pass something by reference then the address of that is pushed on stack instead of creating a copy and when you modify the object in the function then the original object is being modified instead of the copy.

The way it works is that compiler translates your references to variable to indirect memory access when it sees that the argument was passed by ref.

For example let us assume at memory location 100 you have an integer 123; now when you call a function which accepts in by value (which is default) then copy of 123 will be made and pushed on stack before the function is called which means the copy will now be at (lets say) 160 address. However when you pass something by reference then address 100 will be pushed on stack and will reside on location 160.

Now the instructions generated will read 160 to get the location of object and then modify the data at 100. Indirection will happen same as it is done for pointers when you use * operator.

人海汹涌 2024-12-13 19:27:04

通过 ref 意味着您可以为传递的对象创建新实例,
按值不能仅更改对象属性

by ref means you can create new instance to the object passed,
by value cant do that just change the object properties

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文