考虑以下代码(为简单起见,我没有遵循任何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
关键字来获得所需的结果?
答案 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");
}
}