引用类型仍然需要通过ref?

时间:2009-09-04 23:00:30

标签: c#

考虑以下代码(为简单起见,我没有遵循任何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");
    }
}

正如所料,输出是:

  

在调用方法之前教授详细信息()......

     

姓名=大卫

     

调用方法教授详情()......

     

姓名= Flower

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

6 个答案:

答案 0 :(得分:34)

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

当您使用'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 );
}

答案 1 :(得分:2)

你已经通过参考和参考类型混合了。

通过改变p,你不会改变p指向的东西,而是p本身指向的东西,可以这么说。并且因为p尚未声明为ref,所以引用(引用类型)按值传递,而对p的更改不会反映在调用ProfessorDetails的代码中。对实例p的更改指向 反映(因为它是引用类型)。教授是不是值类型,甚至在调用代码中也不会显示这些更改。

答案 2 :(得分:2)

传递引用和引用引用之间存在差异。

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

当您通过引用传递对象时,被调用者会收到对引用的引用。被调用者具有指向原始引用的指针,因此可以修改引用(从而更改引用所指向的对象)以及修改引用所指向的对象。

答案 3 :(得分:1)

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

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

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

答案 4 :(得分:1)

关于'传递引用类型'与'传递引用(通过使用ref关键字)',在我的研究之后,我带走的是:

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

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

答案 5 :(得分:0)

每个引用类型都是按值传递给方法调用。因此,您可以修改实例中的数据,因为它指向同一个地方,但如果要修改实例,则应使用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");
    }
}