9

到目前为止,这是我的理解:

传递价值

按值传递意味着传递参数的副本。对该副本的更改不会更改原件。

通过引用

通过引用传递意味着对原始的引用被传递。对参考的更改会影响原件。

REF 关键字

REF 告诉编译器对象在进入函数之前已经初始化。REF 表示该值已设置,因此该方法可以读取并修改它。REF 有两种方式,既进又出。

OUT 关键字

OUT 告诉编译器对象将在函数内部初始化。OUT 表示该值尚未设置,因此必须在调用 return 之前设置。OUT只是一种方式,就是out。

问题

那么在什么情况下你会结合使用 ref 和 out 关键字,通过引用传递还是按值传递呢?例子会有很大帮助。

非常感谢帮助。

4

7 回答 7

20

永远不会在 1 个参数上组合ref和。out它们都表示“通过引用”。

您当然可以在一种方法中组合 ref 参数和 out 参数。

ref和的区别out主要在于意图。ref 表示 2 路数据传输,out 表示 1 路。

但除了意图之外,C# 编译器会跟踪明确赋值,这会产生最显着的差异。它还可以防止误用(读取)输出参数。

void SetOne(out int x) 
{
  int y = x + 1; // error, 'x' not definitely assigned.
  x = 1;         // mandatory to assign something
}

void AddTwo(ref int x)
{
    x = x + 2;  // OK, x  is known to be assigned
}

void Main()
{
    int foo, bar;

    SetOne(out foo); // OK, foo does not have to be assigned
    AddTwo(ref foo); // OK, foo assigned by SetOne
    AddTwo(ref bar); // error, bar is unassigned
}
于 2009-11-05T20:38:07.240 回答
8

非常感谢帮助

正确和谨慎地使用语言会提高你的理解力。

按值传递意味着传递参数的副本。

是的,这完全准确。

对该副本的更改不会更改原件。

不完全是。首先要仔细区分值变量。考虑:

class Foo { public int x; }
...
void N() 
{
  Foo blah = new Foo();
  blah.x = 0;
  M(blah);
}
...
void M(Foo foo)
{
  foo.x = 123; // changes blah.x
  foo = null; // does not change blah
}

这里的变量是 x、blah 和 foo。x 是一个字段,blah 是一个本地,foo 是一个形式参数。

这里的值是 null、0、123,以及对 Foo 实例的引用。 该参考是一个价值。了解这一事实至关重要。

通过将变量 blah 的值复制到变量 foo 中来传递 blah 值的副本。blah 的值是对 Foo 实例的引用。

M 可以更改变量 x 的值,因为 M 具有 blah 值的副本,它是对 Foo 的引用。当 M 将 foo 的内容更改为 null 时,不会改变 blah;foo 包含 blah 值的副本。

通过引用传递意味着对原始的引用被传递。

仔细选择你的措辞。什么是“原作”?

最好将其表述为“通过引用传递意味着传递了对必须是变量的参数的引用”。一种更简单的思考方式是“通过引用传递使参数成为作为参数传递的变量的别名”。

对参考的更改会影响原件。

由于形参和实参互为别名,因此对引用的更改不会影响原始值;参考是原件。它们都是同一个变量

REF 告诉编译器对象在进入函数之前已经初始化。

“对象”毫无意义。你的意思是“变量”。

“ref”不会“告诉编译器变量已初始化”。相反,“ref”告诉编译器“您,编译器,必须验证变量是否已初始化”。那是相当不同的!

REF 表示该值已设置,

不, ref 要求变量已经设置。没有“设定价值”之类的东西。

因此,该方法可以读取并修改它。

“它”的意思是“变量”。

REF 有两种方式,既进又出。

正确的。

OUT 告诉编译器对象将在函数内部初始化。

停止使用“对象”来表示“变量”。如果你停止混淆完全不同的事物,你会更清楚地理解事物。变量不是对象。变量是存储位置,其中一些可能包含,而其中一些值可能是对对象的引用

所以,out 告诉编译器变量将在方法内部初始化,是的,但这并不完全正确。您忘记了该方法将引发异常或该方法将进入无限循环的情况——这些也是合法的情况。

OUT 表示该值尚未设置,

同样,“值”是指“变量”。但这并不准确。将初始化变量作为“out”参数传递是完全合法的。无意义,但合法。

因此必须在调用 return 之前设置。

不调用“return”;方法被调用。但是是的,该方法必须在正常返回之前为变量赋值。

OUT只是一种方式,就是out。

对。

那么在什么场景下你会结合使用 ref 和 out 关键字

没有这样的场景。

于 2009-11-05T21:34:23.583 回答
1

编辑: 我已经更正了这个答案,以反映 C# 中的 'out' 关键字没有达到您的预期(例如,计算机科学意义上的真正 OUT 参数)。我最初声明“out”是按值传递 OUT 但被证明是错误的。

您不能同时使用“out”和“ref”。C#(NET Framework)中有三种调用约定:

  • 无关键字 = 按值传递 (IN)
  • 'out' 关键字 = 通过引用传递 (REF),调用前没有明确的分配要求
  • 'ref' 关键字 = 通过引用传递 (REF),调用前有明确的分配要求

C# 没有真正的 OUT 或 IN-OUT 参数能力。

要查看 C# 中的“out”参数不是真正的 OUT 参数,可以使用以下代码:

  public class Test
  {
    Action _showValue;

    public void Run()
    {
      string local = "Initial";
      _showValue = () => { Console.WriteLine(local.ToString()); };

      Console.WriteLine("Passing by value");
      inMethod(local);

      Console.WriteLine("Passing by reference with 'out' keyword");
      outMethod(out local);

      Console.WriteLine("Passing by reference with 'ref' keyword");
      refMethod(ref local);

    }

    void inMethod(string arg)
    {
      _showValue();
      arg = "IN";
      _showValue();
    }

    void outMethod(out string arg)
    {
      _showValue();
      arg = "OUT";
      _showValue();
    }

    void refMethod(ref string arg)
    {
      _showValue();
      arg = "REF";
      _showValue();
    }
  }

输出是:

Passing by value
Initial
Initial
Passing by reference with 'out' keyword
Initial
OUT
Passing by reference with 'ref' keyword
OUT
REF

如您所见,'out' 和 'ref' 实际上都经过 REF。唯一的区别在于编译器如何为明确的赋值目的处理它们。

于 2009-11-05T20:39:25.087 回答
1

您了解通过任何一种方式的动态。一些参数场景可能是:

  • ref int num对于输入/输出参数。该函数可以修改其中的值。
  • out int num像 ref except 函数必须在返回之前为其赋值。

一般来说,输出参数适用于函数必须返回多个值的情况,因为函数只有一个返回值(尽管它可以是复合的)。

有时,数据访问提供程序(如某些 ADO.NET 方法)使用输出参数来传递信息。一些数据访问方法模仿具有输入/输出参数的数据库存储过程。

编辑:引用类型的一个规定是参数ref StringBuilder wordStringBuilder word(按值)行为相同 - 外部的字符串受到影响,尽管底层实现可能略有不同,因为此时焦点是引用而不是堆上的值。

于 2009-11-05T20:43:31.623 回答
0

您通过 ref 传递您希望由另一个函数读取和写入的东西,因此您应该传递初始化的变量以便正确读取。

编辑:示例:

void mymethod(ref int a) {
  a++;
}

您将希望由另一个函数写入的内容作为 OUT 传递,但您不需要初始化它,因为它不会被函数读取,只会被写入。

编辑:示例:

void mymethod2(out string a) {
  a="hello";
}
于 2009-11-05T20:38:33.917 回答
0

OUT如果您有一个需要返回多个值的方法,则使用关键字很有用。例如,查看类似int.TryParse().

使用REF更多是为了明确对象。请记住,传递给方法的任何非基元本质上都是通过引用传递的,在普通托管代码中并没有太多需要它。通过声明 REF 关键字,您声明参数可能会在方法的主体中被修改,因此调用代码应该知道它(因此您也必须ref在调用代码中显式添加 。

于 2009-11-05T20:41:52.983 回答
0

如果您了解 C++,也许这会对您有所帮助:

void Foo(Bar) {} // pass by value c# (if it's a value type ;))
void Foo(Bar) {} // c++

void Foo(Bar) {} // pass by reference c# (if it's a reference type ;))
void Foo(Bar&) {} // c++

void Foo(ref Bar) {} // c#
void Foo(Bar*) // c++

void Foo(out Bar) {} // c#
void Foo(Bar**) {} // c++ (the contents of *Bar needs to be filled up)
于 2009-11-05T20:47:22.690 回答