【问题标题】:Reference type still needs pass by ref?引用类型还需要通过 ref 传递吗?
【发布时间】:2010-11-25 19:06:41
【问题描述】:

考虑以下代码(为简单起见,我没有遵循任何 C# 编码规则)。

public class Professor
{
    public string _Name;
    
    public Professor(){}

    public Professor(string name)
    {
        _Name=name;
    }

    public void Display()
    {
        Console.WriteLine("Name={0}",_Name);
    }
}

public class Example
{
    static int Main(string[] args)
    {
        Professor david = new Professor("David");

        Console.WriteLine("\nBefore calling the method ProfessorDetails().. ");
        david.Display();
        ProfessorDetails(david);
        Console.WriteLine("\nAfter calling the method ProfessorDetails()..");
        david. Display();
    }

    static void ProfessorDetails(Professor p)
    {
        //change in the name  here is reflected 
        p._Name="Flower";
    
        //Why  Caller unable to see this assignment 
        p=new Professor("Jon");
    }
}

正如预期的那样,输出是:

调用ProfessorDetails()方法之前...

姓名=大卫

调用ProfessorDetails()方法后...

名字=花

ProfessorDetails(Professor p) 中的调用p=new Professor("Jon"); 无效,即使它是引用类型。为什么我仍然需要使用ref 关键字来获得想要的结果?

【问题讨论】:

标签: c#


【解决方案1】:

在 C# 中,一切都是按值传递的。但是,当您传递引用类型时,引用本身是由 value 传递的,即传递原始引用的 副本。因此,您可以更改引用副本指向的对象的状态,但如果您为引用分配新值,您只会更改副本指向的内容,而不是原始引用。 p>

当您使用 'ref' 关键字时,它会告诉编译器传递原始引用,而不是副本,因此您可以修改引用指向函数内部的内容。但是,这种需求通常很少见,通常在您需要从一个方法返回多个值时使用。

一个例子:

class Foo
{
    int ID { get; set; }

    public Foo( int id )
    {
        ID = id;        
    }
}

void Main( )
{
    Foo f = new Foo( 1 );
    Console.WriteLine( f.ID );  // prints "1"
    ChangeId( f );
    Console.WriteLine( f.ID );  // prints "5"
    ChangeRef( f );
    Console.WriteLine( f.ID );  // still prints "5", only changed what the copy was pointing to
}

static void ChangeId( Foo f )
{
    f.ID = 5;
}

static void ChangeRef( Foo f )
{
    f = new Foo( 10 );
}

【讨论】:

  • 正是我要说的。我敢肯定 Jon Skeet 会用另外一个问题进行实地考察。
  • 大声笑,是的,我自己回答了几个问题。这个概念经常被误解。
  • 我认为这可能是 .NET 中最常见的误解,如果您在开始 .NET 之前应该阅读一件事,那就是这个概念;)
  • 谢谢 Ed Swangren,我从你的 expln 中得到了它
  • 如果你能提供一个关于when you need to return multiple values from a method的例子就更好了。
【解决方案2】:

您已经混淆了引用传递和引用类型。

通过改变 p,你并没有改变 p 指向的东西,而是 p 本身指向的地方,可以这么说。并且因为 p 还没有被声明为 ref,所以引用(对引用类型)是按值传递的,对 p 的更改并没有体现在调用 Prof.Details 的代码中。对 p 指向的实例的更改被反映在 上(因为这是一个引用类型)。教授会不会是一个值类型,即使是那些更改在调用代码中也不可见。

【讨论】:

    【解决方案3】:

    传递引用和对引用的引用是有区别的。

    当你传递一个(引用类型的)对象时,被调用者可以通过底层指针修改对象数据,但是如果被调用者修改了引用,当函数返回时,调用者不会从堆栈中读取更改的引用.被调用者不能改变被引用的对象。

    当您通过引用传递对象时,被调用者会收到对该引用的引用。被调用者有一个指向原始引用的指针,所以除了修改引用指向的对象之外,还可以修改引用(从而改变引用指向的对象)。

    【讨论】:

      【解决方案4】:

      p 的实际值是对与 david 相同的教授实例的引用。您对该引用进行的任何调用都将被取消引用,因为调用与对 david be 进行的调用相同的实例。但是,p 是该引用的副本,它与 david 值不同。

      因此,当您执行 p = new Professor() 时,您正在更改引用变量的值以指向一个新实例。但是,这不会修改 david 引用,它仍然指向旧实例。

      如果您将 p 作为 ref 传递,则 p 的值将是对 david 引用变量的引用。修改它实际上会修改 david 值以指向一个新实例。

      【讨论】:

        【解决方案5】:

        关于“传递引用类型”与“通过 ref(通过使用 ref 关键字)传递”,经过我的研究,我的结论是:

        如果你有一个引用类型的对象,并且保持这个对象从一个方法传递到另一个方法,那么整个时间对象都指向内存的某个位置。例如,如果您通过更改属性值来处理此对象,这将导致对原始对象的更改。想一想,在不同的方法中,你一直在谈论同一个人;在一种方法中,你改变了那个人衬衫的颜色。所以这也会导致原始人对象的变化。

        但是,在您从一种方法跳转到另一种方法的过程中,如果您为对象创建一个新引用(就像您通过编写 'p=new Professor("Jon")' 所做的那样),您基本上是在打破新方法中的对象与原始对象之间的链接。您的“p”现在引用内存中的另一个位置。因此,无论您在内存的这个新位置所做的任何更改,都不会对原始对象产生任何影响。但是,如果要更改原始对象地址并获得链接,则需要使用 ref 关键字。小心使用 REF 关键字,因为一旦在任何方法中,您将内存中的原始地址更改为新地址(通过使用 ref 关键字),其他方法中对原始对象所做的所有更改现在都消失了。

        【讨论】:

          【解决方案6】:

          每个引用类型都按值传递给方法调用。所以你可以修改实例内部的数据,因为它指向同一个地方,但是如果你想修改实例,你应该使用 ref

          public class Professor
          {
              public string _Name;
          
              public Professor(){}
          
              public Professor(string name)
              {
                  _Name=name;
              }
          
              public void Display()
              {
                  Console.WriteLine("Name={0}",_Name);
              }
          }
          
          public class Example
          {
              static int Main(string[] args)
              {
                  Professor david = new Professor("David");
          
                  Console.WriteLine("\nBefore calling the method ProfessorDetails().. ");
                  david.Display();
                  ProfessorDetails(ref david);
                  Console.WriteLine("\nAfter calling the method ProfessorDetails()..");
                  david. Display();
              }
          
              static void ProfessorDetails(ref Professor p)
              {
                  //change in the name  here is reflected 
                  p._Name="Flower";
          
                  //Why  Caller unable to see this assignment 
                  p=new Professor("Jon");
              }
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2014-01-25
            • 2011-03-25
            • 1970-01-01
            • 2019-04-21
            • 2011-11-02
            • 2020-04-01
            • 1970-01-01
            • 2020-07-02
            相关资源
            最近更新 更多