C# 何时传入ref关键字

C# 何时传入ref关键字,c#,C#,我已经读过passng和not passing ref in参数之间的区别,但是,我想什么时候使用它们呢 例如,我在一个方法中有一些逻辑,可以重构成它自己的方法。Resharper 4.5将其中一个参数设置为ref类型,但我认为如果我手动进行重构,我不会这样做 显然,我缺少一些理解。例如,当编码中的某些类型或某些场景缺少ref关键字时,会发生什么情况 感谢传递byref仅对函数的“副作用”有意义:即,您打算修改一个值类型参数,或将另一个对象重新分配给给定的对象参数,并使该更改在函数调用后仍然有效

我已经读过passng和not passing ref in参数之间的区别,但是,我想什么时候使用它们呢

例如,我在一个方法中有一些逻辑,可以重构成它自己的方法。Resharper 4.5将其中一个参数设置为ref类型,但我认为如果我手动进行重构,我不会这样做

显然,我缺少一些理解。例如,当编码中的某些类型或某些场景缺少ref关键字时,会发生什么情况


感谢

传递
byref
仅对函数的“副作用”有意义:即,您打算修改一个值类型参数,或将另一个对象重新分配给给定的对象参数,并使该更改在函数调用后仍然有效。示例:
TryGetValue()


否则,最好在概念上坚持使用
byval

,区别在于值类型直接存储其值,而引用类型存储对值的引用。也许您应该重新阅读一些关于引用与值类型的内容

通过引用传递值类型(如上所示)很有用,但ref对于传递引用类型也很有用。这允许被调用的方法修改引用所引用的对象,因为引用本身是通过引用传递的。下面的示例显示,当引用类型作为ref参数传递时,可以更改对象本身


结果是,当控件传递回调用方法时,对方法中参数的任何更改都将反映在该变量中。因此,如果您希望分配给参数的值在方法调用之后继续存在,这是一个可能的用例,让我将其分解为两个问题:

1) 在编写方法时,何时应该使用ref/out形式参数声明

如果希望方法能够读取和写入从调用方传入的变量,而不仅仅是读取值,请使用ref/out

2) 为什么“提取方法”重构会产生ref参数

我不知道Resharper的细节,但我可以猜一猜。考虑以下邪恶可变值类型:

struct S 
{ 
  private int x;
  public int X() { return this.x; } 
  public void M() { this.x += 1; } 
}
您有一个方法:

void Foo() 
{
    S s = new S();
    Fred(s);
    Blah(s);
    Bar(s);
    s.M();
    Console.WriteLine(s.X()); // prints 1
}
在中间位置执行“提取方法”:

void NewMethod(ref S s)
{
    Blah(s);
    Bar(s);
    s.M();
}

void Foo() 
{
    S s = new S();
    Fred(s);
    NewMethod(ref s);
    Console.WriteLine(s.X()); // still prints 1
}
如果您创建了一个没有“ref”的方法,那么调用NewMethod会将s的副本传递给NewMethod。记住,值类型是按值复制的;这就是为什么我们称它们为“值类型”。它将是变异的副本,然后s.X()返回零。对于重构来说,在程序中引入语义变化是个坏主意,重构引擎很难知道给定的方法是否依赖于值类型的可变性

这只是您应该避免可变值类型的另一个原因。

考虑以下示例:

static int i = 3;

public static void ChangeIntRef(ref int val)
{
   val = 5;
}

public static void ChangeInt(int val)
{
   val = 5;
}

Console.WriteLine(i);
ChangeInt(i);
Console.WriteLine(i);

ChangeIntRef(ref i);
Console.WriteLine(i);
通过以
ref
的形式传递参数,您告诉编译器您真正想要的是传递给方法的原始变量的引用。因此,该方法可以更改原始变量的值

如果从上面运行代码段,结果是:

3
3
5

这应该清楚地表明,如果没有
ref
关键字,
ChangeInt
方法实际上无法更改原始值。但是,使用
ref
关键字
ChangeIntRef
方法可以更改原始值。

我使用引用来表示语义。考虑这种方法:

void AddResultsTable(ref PlaceHolder p) // modifies p; adding a table
{
    var t = new Table();

    AddTableHeader(ref t); // modifies t; adding a table header

    AddTableBody(ref t);  // modifies t; adding a table body

    AddTableFooter(ref t);  // modifies t; adding a table footer

    p.Controls.Add(t);
}

AddResultsTable(ref PlaceHolderResults);
与此相比:

Table ReturnTable()
{
    var t new Table();

    // AddTableHeader() returns TableHeader
    t.Columns.HeaderColumns.Add(ReturnTableHeader());

    // ... etc.

    return t;
}

PlaceHolder.Controls.Add(ReturnTable());

第一段代码在我看来更清晰;方法修改对象,而不是返回您必须添加的新对象。在方法内部,它都保持“封闭”和隐藏状态。

以及何时传递
?@Natrium:我发现时间是在你把几个到多个参数都扔回去之后:)实际上
TryGetValue()
使用
out
关键字,调用者不需要初始化变量,但被调用者是。
ref
关键字用于被调用方可能更改对象引用的情况。
Table ReturnTable()
{
    var t new Table();

    // AddTableHeader() returns TableHeader
    t.Columns.HeaderColumns.Add(ReturnTableHeader());

    // ... etc.

    return t;
}

PlaceHolder.Controls.Add(ReturnTable());