我使用过Java,C ++ ,. Net。 (以该顺序)。当被问及关于面值的副值和副率时,我总是在这个问题上做得很好......也许是因为没有人深入研究它。现在我知道我看不到全貌。
我正在查看其他人编写的这段代码:
XmlDocument doc = new XmlDocument();
AppendX(doc); // Real name of the function is different
AppendY(doc); // ditto
当我看到这段代码时,我想:等一下,我不应该在ref
变量前面使用doc
(并相应地修改AppendX/Y
吗?它的工作方式是书面的,但让我怀疑我是否真正了解C#中的ref
关键字。
当我想到这一点时,我回想起早期的Java时代(大学介绍语言)。我的一个朋友看了我写的一些代码,他有一个心理障碍 - 他一直在问我哪些东西是通过引用传递的,什么时候通过值传递。我无知的反应是这样的: Dude,在Java中只有一种arg传递,我忘了它是哪一个:)。冷静,不要过度思考,只需编码。
Java仍然没有ref
吗?然而,Java黑客似乎是富有成效的。无论如何,C ++中的编码通过参考业务将我暴露给了这一整体,现在我很困惑。
上面的例子中应该使用ref
吗?
我猜测当ref
应用于值类型时:基元,枚举,结构(此列表中还有其他内容吗?)它会产生很大的不同。并且...当应用于对象时它不会,因为它全部是通过引用。如果事情如此简单,那么为什么编译器不会将ref
关键字的使用限制为类型的子集。
说到对象,ref
是否可以作为评论?好吧,我确实记得null
可能存在问题,而ref
对于初始化方法中的多个元素也很有用(因为你不能像在Python中那样容易地返回多个东西)
感谢。
答案 0 :(得分:10)
你并不孤单。
默认情况下,所有内容都是通过C#中的值传递的 - 但是对于引用类型(例如XmlDocument
),该值是引用。
ref
关键字用于表示参数是“按引用传递”,并且还必须在呼叫站点指定。 Java没有任何等价物 - Java中的所有内容都是按值传递的。
有关详细信息,请参阅我的article on parameter passing。
答案 1 :(得分:4)
对象类型总是通过引用传递(实际上引用是按值传递的)。积分类型可以通过引用或值传递。
答案 2 :(得分:4)
接受ref
传递的引用类型变量的方法可能会更改变量指向的内容 - 而不仅仅是修改它。
答案 3 :(得分:2)
不,不要使用ref关键字。您想要使用doc的内容,而不是更改doc本身。请参阅此帖子以获取进一步的解释:ref keyword
答案 4 :(得分:2)
这取决于你想要AppendX做什么。如果它正在修改对象的内容,则不需要通过ref传递。如果您希望AppendX能够更改变量“doc”指向的对象,则需要。 “doc”已经是一个“引用类型”,它等同于它是c ++中对象的指针。
答案 5 :(得分:2)
http://www.albahari.com/valuevsreftypes.aspx对.Net中参考类型和值类型之间的区别有很好的解释。
答案 6 :(得分:1)
我和一些同事争论这件事并最终失败;这是我学会按照自己的方式做事的方式。
ref
和out
关键字表示“我可能/将使用新参考替换当前参考”。调用该方法后,该变量可能会引用一个完全不同的对象。另一方面,总是知道对于引用类型,该类型的属性可能会改变,如果它们很重要,则应该缓存它们。
我并不完全赞同,但有一些东西可以表示“参考会改变”。
答案 7 :(得分:1)
你并不是唯一一个在c ++和c#中被混淆和编码的人,当你使用& c时,我可以理解为什么(不是作为批评而是评论,因为那是我生活的世界)。在传递引用的参数上,你基本上说这是一个别名,我将在方法中使用该参数传递给方法。 你对该参数做的任何都会产生与你自己使用变量相同的效果。 所以在代码中你可以这样做: void Foo(MyClass& arg) { arg = new MyClass(1); }
int x = new MyClass(0);
Foo(x);
或
int x = new MyClass(0);
void Foo()
{
x = new MyClass(1);
}
在任何一种情况下x现在等于MyClass(1)(并且你有一个泄漏因为你无法获得原始但这不是我的观点)。我想你已经知道了这个问题,但无论如何它都会有用。)
如果在标准中传递引用,则引用按值传递。它不再是别名,您对引用的对象所做的一切都将影响对象,但如果您对引用该对象的变量执行任何操作(例如,分配新对象),那么这只会影响引用的副本。我们有更多的代码
C#
MyClass x = MyClass(0);
void Foo(MyClass arg) //reference being passed by val
{
arg = new MyClass(1);
}
Foo(x);
x仍然等于MyClass(0)
MyClass x = MyClass(0);
void Foo(ref MyClass arg) //passing by ref
{
arg = new MyClass(1);
}
Foo(ref x);
x等于MyClass(1)
因此,传递C#的标准参数与在C ++中传递引用不同,但使用ref关键字(不鼓励)会使您接近与&相同的机制。在c ++中 &安培;由于优化/缺少复制,通常鼓励使用C ++,因为你只复制C#中不需要关注的引用,只有当你真正需要传递给方法的变量的别名时才使用ref必须为变量分配新的对象实例,而不是使用/更改对象的状态
答案 8 :(得分:1)
如果您之前使用C ++编码,那么您必须熟悉指针。 .NET代码和Java中的对象引用是引擎盖下的指针。它只是没有明确地用语法编写,这使得它显然是一个指针,你应该记住它。规则不是很难,任何引用类,数组,字符串或System.Object的对象的变量都是引用类型,变量是指针。其他任何东西都是值类型,变量包含实际值。
将这样的变量传递给方法时,传递的是指针值。然后,该方法可以在其认为合适时修改指向对象。通过引用传递指针没有任何区别,它仍然是指向同一对象的相同指针。
传递值类型的值时,这完全不同。如果希望调用方法修改该值,则必须生成指向该值的指针。您可以在方法声明中使用“ref”关键字。
异常情况是您希望调用方法返回新对象的位置。换句话说,修改指针值。 然后你必须使用ref关键字,它创建一个指向指针的指针。您通常会通过让方法返回对象作为方法返回值来避免这种情况。
答案 9 :(得分:1)
很多人对此感到困惑。这是我如何看待它。我根本不认为“参考”意思是“参考”。我认为“ref”意思是“别名”。也就是说,当你说
时void M(ref int x) { x = 123; }
void N(int z) { ... }
...
int y = 456;
M(ref y);
N(y);
“ref y”的含义是“请将相应的形式参数x alias 设为变量y”。也就是说,x和y现在都是相同存储位置的变量。当您写入x时,您正在写y,因为x是y的另一个名称。
当你没有参考时传递,就像在N(y)中一样,你说“y和z是两个不同的变量,这样z开始它的生命周期,其内容与y相同”。
一旦你开始考虑它就像你不必担心传递参考值和传递值一样,等等等等等,这一切都让人非常困惑。正常传递创建一个新变量并使用参数初始化它,而ref 创建一个现有变量的别名。
我希望我们使用“alias”代替“ref”;它会更清楚。