繁体   English   中英

何时传递ref关键字

[英]When to pass ref keyword in

我已经阅读了passng和不在参数中传递ref之间的区别,但是,我何时想要使用它们?

例如,我在一个方法中有一些逻辑可以重构为它自己的方法。 Resharper 4.5将参数之一作为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。 请记住,值类型按值复制; 这就是为什么我们称它们为“价值类型”。 它将是变异的副本,然后sX()返回零。 重构在程序中引入语义变化是一个坏主意,重构引擎很难知道给定方法是否依赖于值类型的可变性。

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

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

否则,最好坚持使用byval

从概念上讲,不同之处在于值类型直接存储其值,而引用类型存储对该值的引用。 也许你应该重读一下有关参考值和值类型的信息。

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

   class RefRefExample
{
    static void Method(ref string s)
    {
        s = "changed";
    }
    static void Main()
    {
        string str = "original";
        Method(ref str);
        // str is now "changed"
    }
} 

MSDN

结果是,当控制传递回调用方法时,方法中参数的任何更改都将反映在该变量中。 因此,如果您希望分配给参数的值在方法调用之后继续存在,那么它就是一个可能的用例

考虑这个例子:

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());

第一段代码对我来说看起来更干净; 方法修改对象而不是返回您必须添加的新对象。 它们都在方法中保持“盒装”并隐藏起来。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM