【问题标题】:Pass-by-reference and ref [duplicate]通过引用和参考 [重复]
【发布时间】:2014-05-11 18:08:21
【问题描述】:

我一直在阅读 MSDN 上的一些教程,以了解 C#、refout 中的传递引用,我遇到了以下代码示例:

using System;

class TheClass
{
    public int x;
}

struct TheStruct
{
    public int x;
}

class TestClass
{
    public static void structtaker(TheStruct s)
    {
        s.x = 5;
    }
    public static void classtaker(TheClass c)
    {
        c.x = 5;
    }
    public static void Main()
    {
        TheStruct a = new TheStruct();
        TheClass b = new TheClass();
        a.x = 1;
        b.x = 1;
        structtaker(a);
        classtaker(b);
        Console.WriteLine("a.x = {0}", a.x); //prints 1
        Console.WriteLine("b.x = {0}", b.x); //prints 5
    }
}

教程中对此的注释:

这个例子展示了当一个结构被传递给一个方法时,一个 结构体被传递,但是当一个类实例被传递时,一个引用 已通过。

我完全理解,但我的问题是,如果在 C# 中将引用传递给参数,为什么他们需要ref,如下例所示:

void tearDown(ref myClass a)
{
    a = null;
}

MyClass b = new MyClass();
this.tearDown(ref b);
assert(b == null);
//b is null

???我认为 C# 在 C 中是相同的——按值传递。

【问题讨论】:

  • 仅仅因为它是引用类型,并不意味着它是通过引用传递的。除非您明确使用ref,否则按值传递。
  • @JeroenVannevel 是的,默认情况下是按值传递的,但是传递的值本身就是引用类型的引用。
  • MyClass b = new MyClass(); 部分创建 2 块内存:引用变量 b 和堆上的匿名实例。引用变量的行为与值类型完全一样(例如int)。
  • Jeroen,不,先生,这不正确。对象通过引用自动传递。要对其进行测试,只需将可变对象(例如字符串列表)作为参数传递给方法。在方法的列表中添加一个项目。然后检查方法之外的列表,您会看到列表已被修改。这仅在通过引用传递对象时发生。引用类型(即所有对象)自动通过引用传递。
  • 当您删除 ref 时,您的 assert(b == null); 将失败。 TearDown() 需要通过引用来引用。

标签: c#


【解决方案1】:

在 C# 中,基本上所有的类都是指针。但是,是否通过 ref/out 传递就像将指针传递给指针或指针本身一样。

当您传递一个类时(根据第一个示例),对类成员的任何更改都会被保留。但是,更改对对象本身的引用不会产生结果。说你替换

public static void classtaker(TheClass c)
{
    c.x = 5;
}

public static void classtaker(TheClass c)
{
    c = new TheClass();
    c.x = 5;
}

由于c 不是outref 参数,因此您将本地指针重新分配给c,而不是c 本身的值。由于你只修改了本地c.x,所以调用这个修改后的ClassTaker后,结果会是b.x == 1

现在,根据您的第二个示例,由于 a 是 ref 参数,因此将在调用范围内看到对值 a 本身的更改,如示例中所示,但从调用中删除 ref 会导致null 断言失败。

基本上,ref 传递传递可以被认为是指向您的指针的指针,而在没有 ref/out 的情况下调用传递一个复制的指向您的对象数据的指针。


编辑:

可以在方法范围内分配c.X 的原因是因为对象c 指向 对象X,并且无论如何您将始终获得指向X 的相同指针ref/out 参数与否。相反,ref/out 修改了您更改值 c 的能力,如调用范围所见。

【讨论】:

  • 这里是tutorial 的链接,它显示了结果。谢谢。
  • c.X 会发生变化,因为 X 是 c 对象内的指针,并且无论是否 ref.out 都可以在方法范围内进行更改。 ref/out 参数仅修改您更改调用范围所看到的 c 值的能力。我会将其整合到我的答案中
  • 我真的不知道 - 我更熟悉代码的行为,而不是它背后的理论和术语。打赌,我认为这是因为在 C# 中,一半的行为是由对象本身的定义(结构与类)决定的,而不是一个人想要的特定方法或实现(根据 C++)
  • 一些行话修正:类不是指针,它们是类型。对象是类的实例,并且始终是指针。引用(指针)是单独的项目。
猜你喜欢
  • 1970-01-01
  • 2019-07-11
  • 2015-10-09
  • 1970-01-01
  • 2017-03-21
  • 2012-07-24
  • 2015-07-09
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多