C# 使用REF&;通过引用传递OUT关键字&;在C中按值传递# 以下是我到目前为止的理解: 传递值

C# 使用REF&;通过引用传递OUT关键字&;在C中按值传递# 以下是我到目前为止的理解: 传递值,c#,pass-by-reference,ref,out,C#,Pass By Reference,Ref,Out,按值传递意味着传递参数的副本。 对该副本的更改不会更改原件 参照 通过引用传递意味着传递对原始文件的引用。 对参照的更改会影响原始参照 REF关键字 REF告诉编译器对象在进入函数之前已初始化。 REF表示该值已设置,因此该方法可以读取并修改该值。 REF是两种方式,输入和输出 OUT关键字 OUT告诉编译器对象将在函数内部初始化。 OUT表示尚未设置该值,因此必须在调用return之前进行设置。 出去只有一条路,那就是出去 问题: 那么,在什么情况下,您会将ref和out关键字的使用与按引用传

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

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

REF关键字 REF告诉编译器对象在进入函数之前已初始化。 REF表示该值已设置,因此该方法可以读取并修改该值。 REF是两种方式,输入和输出

OUT关键字 OUT告诉编译器对象将在函数内部初始化。 OUT表示尚未设置该值,因此必须在调用return之前进行设置。 出去只有一条路,那就是出去

问题: 那么,在什么情况下,您会将ref和out关键字的使用与按引用传递或按值传递结合起来呢? 举例将大有帮助


非常感谢您的帮助。

您永远不会将
ref
out
组合在一个参数上。它们都表示“通过引用传递”

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

ref
out
之间的区别主要在于意图。ref信号为双向数据传输,out表示单向

但除了意图,C#编译器还跟踪确定的赋值,这是最明显的区别。它还可以防止误用(读取)out参数

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
}

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

编辑:示例:

void mymethod(ref int a) {
  a++;
}
void mymethod2(out string a) {
  a="hello";
}
您可以传递希望由另一个函数写入的内容,但不需要初始化它,因为它不会被函数读取,只会被写入

编辑:示例:

void mymethod(ref int a) {
  a++;
}
void mymethod2(out string a) {
  a="hello";
}

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

不能同时使用“out”和“ref”。C#(网络框架)中有三种调用约定:

  • 无关键字=传递值(IN)
  • “out”关键字=调用前无明确分配要求的按引用传递(REF)
  • “ref”关键字=调用前具有明确分配要求的按引用传递(ref)
C#没有真正的输出或输入输出参数能力

要查看C#中的“out”参数不是true 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传递的。唯一的区别在于编译器如何处理它们以实现明确的赋值目的。

如果您有一个需要返回多个值的方法,则使用
out
关键字非常有用。例如,看看像
int.TryParse()
这样的方法


使用
REF
更多地是为了明确对象。请记住,传递到方法中的任何非原语本质上都是通过引用传递的,在正常的托管代码中不太需要它。通过声明REF关键字,您表示参数可能会在方法体中被修改,因此调用代码应该知道它(因此您也必须在调用代码中显式地添加
REF

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

  • ref int num
    用于输入/输出参数。函数可以修改其中的值
  • out int num
    与ref类似,函数必须在返回前为其赋值
通常,输出参数适用于函数必须返回多个值的情况,因为函数只有一个返回值(尽管它可以是复合的)

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


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

如果你理解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)
非常感谢你的帮助

正确、仔细地使用语言会提高你的理解力

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

是的,这是准确的

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

不完全正确。首先仔细区分值和变量。考虑:

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值的副本,blah值是对Foo的引用。当M将Foo的内容更改为null时,这不会更改blah;Foo包含blah值的副本

通过引用传递意味着传递对原始文件的引用

仔细选择你的措辞,“原文”是什么

这最好表述为“通过引用传递意味着传递对参数的引用,该参数必须是变量”