2009-09-21 57 views
4

我对这段代码有点困惑。c# - ref修饰符...参考类型

public static void Foo(A p) 
    { 
     p.SomeProp = "ccc"; 
     p = null; // !!! 
    } 

    static void Main(string[] args) 
    { 
     A p = new A(); 
     Foo(p); 

     Console.WriteLine("SomeProp is: " + p.SomeProp); 
     Console.ReadLine(); 
    } 

输出是:

“SomeProp是:CCC

但我本来期望一个NullReferenceException。


但是,如果我改变它,像这样,使用ref修饰符:

public static void Foo(ref A p) 
    { 
     p.SomeProp = "ccc"; 
     p = null; 
    } 

    static void Main(string[] args) 
    { 
     A p = new A(); 
     Foo(ref p); 

     Console.WriteLine("SomeProp is: " + p.SomeProp); 
     Console.ReadLine(); 
    } 

我得到一个NullReferenceException - 第二个是可以理解我。

但是怎么会这样呢,在第一段代码中,p没有设置为空,但是该属性获取了它的值集?

我的问题是:如果第一段代码中的Foo方法不是对原始实例的引用,那么它的p参数是什么?


Btw。这里是A类的定义

public class A 
{ 
    public string SomeProp; 
} 
+0

另请参阅:http://stackoverflow.com/questions/186891/why-use-ref-keyword-when-passing-an-object – 2009-09-21 15:56:49

回答

5
p.SomeValue = "ccc"; 

是说:

  • 获取使得p对象就是
  • 基准组对象的属性someValue中的值设定为“CCC”

    p = null;

是说:

  • 变化p到,而不是指它使用的对象,现在指空。

这并不是说改变p指向null的对象,而是局部变量p现在应该引用null。

默认情况下,当您在方法调用“Foo(p)”中传递类型A的参数时,不会传递由p引用的对象,或者引用p,但引用由p引用的对象。它们引用同一个对象,但它们不是同一个参考。即“public static void Foo(A p)”中的引用p与“Foo(p)”中的p不是相同的引用,但它们引用相同的对象。

您可以通过改用ref参数来改变这种行为。这改变它使得它们是相同的参考,并且改变其值的值改变另一个的值。

+0

希望我现在得到它,感谢你和所有其他人的解释 – Max 2009-09-22 07:37:12

1

在第一个函数中,您只是将p的本地引用设置为null。你没有将主要设置为null。在第二个函数中,由于使用了ref,所以你的将p设置为null。

17

在.Net中,一切通过值传递,除非您明确使用refout关键字。对于参考类型,这意味着传递参考的副本。

在你的第一个例子中,这意味着你的p变量仍然是对同一个对象的引用,因此设置一个属性就像你期望的那样工作。但是,当您将引用设置为null时,所有更改都是副本。

+0

+1因为短而准确的答案 – Max 2009-09-22 07:36:37

1

除非您使用out/ref,否则C#使用值传递。当你通过价值传递参考。该参考被复制。但是,由于它仍然引用堆中的同一对象,因此可以通过引用修改对象的状态。如果您使用ref您传递参考地址。如果在此之后访问原始引用,则取消该引用将会删除实际引用,因此会删除NullReferenceException

3

p参数是对您创建的新Foo实例的引用的副本。把它想象成一个路标:调用“new A()”在堆上创建一个A对象,并且给你一个路标给它,你存储在p中。然后你调用Foo函数,给它一个路标的副本 - 它知道如何去A对象,并更新属性。然后它在整个路标上涂鸦 - 它不能再到那里了,但是物体依然存在。调用者仍然有一个有效的路标,所以不会引发异常。

第二个例子,用“ref”参数有效地说“不要给我一个路标的副本,给我实际的路标”。这一次,当它遍布它时,传递给该函数的原始信息也会丢失,并发生异常。

在更多的技术术语,C#中,没有裁判的关键字,总是“按值调用” - 所有参数都传入的参数值的副本。争论的价值是一个路标(或“参考”)的事实并不重要。

+0

+1为好的路标例如,它帮助了我很多 – Max 2009-09-22 07:35:52

4

对象引用按值传递。 < - 非常重要。

你的方法有复制引用,这样的,当你说p = null,只更改该引用的副本。当您返回时,原始参考“p”仍具有原始值。

在第二个例子中,通过参考明确地传递你的对象。所以,新的引用(null)被传回给你的调用函数(然后给你null异常)。

-1

当你路过valule你可以改变其状态的对象引用。但是当你通过引用传递一个对象引用时,你不仅可以改变它的状态,还可以改变实际的对象本身

编辑:后知道对象永远不会为值或引用传递,而是一个对象的引用作为值或引用被传递。

0

如何在调试时将“p”添加到监视列表并为其生成对象ID,该ID与函数Foo(p)中的“p”的ID相同。所以基本上调用方法中的“p”和Foo中的“p”都具有相同的对象ID。