引用types还需要通过ref?
考虑下面的代码(为了简单起见,我没有遵循任何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()后…
名字=花
呼叫p=new Professor("Jon");
在ProfessorDetails(Professor p)
是不是有效的,即使它是参考types。 为什么我仍然需要使用ref
关键字来获得所需的结果?
一切都通过C#中的值传递。 但是,当你传递一个引用types时,引用本身是通过值传递的,即传递原始引用的副本 。 因此,您可以更改参考副本所指向的对象的状态 ,但是如果您为参考指定新值,则只会更改副本指向的内容,而不是原始参考。
当你使用'ref'关键字时,它告诉编译器传递原始引用,而不是副本,所以你可以修改引用指向函数内部的内容。 但是,这种需求通常很less见,而且在需要从一个方法中返回多个值的情况下,这种情况经常被使用。
一个例子:
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 ); }
你已经通过参考和参考types混合起来。
通过改变p,你不会改变p指向的东西,但是p本身指向的地方,可以这么说。 而且因为p没有被声明为ref,引用(对于引用types)是通过值传递的,并且对p的改变不会反映在调用ProfessorDetails的代码中。 实例p指向的更改被反映(因为这是一个引用types)。 教授是一种价值types,即使这些变化在调用代码中也是可见的。
传递引用和引用引用之间有区别。
当你传递一个引用types的对象时,被调用者可以通过基础指针修改对象数据,但是如果被调用者修改了引用,那么当函数返回时,调用者不会从堆栈中读取已经改变的引用。 被调用者不能改变被引用的对象。
当您通过引用传递对象时,被调用者会收到对引用的引用。 被调用者有一个指向原始引用的指针,所以除了修改引用的对象之外,还可以修改引用(从而改变引用指向的对象)。
p的实际价值是与大卫一样的教授实例的参考。 您对该引用进行的任何调用都被解除引用,而对大卫所做的调用则是对同一个实例的调用。 但是,p是该引用的副本,与大卫值不同。
因此,当你做p = new Professor()时,你正在改变引用variables的值来指向一个新的实例。 但是,这并没有修改大卫参考,这仍然指向旧实例。
如果你将p作为parameter passing,那么p的值将是对大卫参考variables的引用。 修改它实际上会修改大卫值指向一个新的实例。
关于“通过引用types”和“通过引用通过引用(通过使用引用关键字)”,我的研究拿走后是这样的:
如果你有一个引用types的对象,并保持这个对象从一个方法传递到另一个方法,整个对象指向内存的某个位置的时间。 如果您通过更改属性值来处理此对象,则会导致更改原始对象。 想一想,用不同的方法你总是在谈论同一个人; 在一种方法中,你改变了这个人的衬衫的颜色。 所以这也会导致原来的人物对象发生变化。
但是,在你从一种方法跳到另一种方法的过程中,如果你为对象创build一个新的引用(就像你正在写'p = new Professor(“Jon”)'),你基本上是断开对象在一个新的方法和原始对象。 你的'p'现在引用到内存中的另一个位置。 所以无论你在这个新的记忆位置做什么改变,它都不会对原始对象产生任何影响。 但是,如果要更改原始对象地址并获得链接,则需要使用ref关键字。 特别是在使用REF KEYWORD的时候,因为一旦用任何方法将内存中的原始地址更改为一个新的地址(通过使用ref关键字),对其他方法完成的原始对象的所有更改现在都消失了。
每个引用types都是按值传递给方法调用的。 所以你可以修改你的实例中的数据,因为它指向同一个地方,但是如果你想修改实例,你应该使用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"); } }