C#构造函数,对象参数是按引用还是按值传递

12

如果您有一个带有对象输入参数的类和构造函数 - 那么该对象是按引用传递还是按值传递?

对于类方法,假设对象输入参数默认情况下是按值传递的,除非使用ref关键字,这是正确的吗?

那么out关键字呢?这是否仍意味着它是按引用传递的?


2
Skeets在这里提供了所有你需要知道的内容:http://www.yoda.arachsys.com/csharp/parameters.html - SwDevMan81
7个回答

20
如果你有一个类和一个构造函数,该构造函数以对象作为输入参数 - 那么这个对象是按引用传递还是按值传递?
除非参数被标记为 outref,否则在 C# 中所有参数都是按值传递的。这是一个非常令人困惑的地方。我来更明确地说明一下:所有参数都会复制其值,除了那些被标记为 outref 的参数。对于值类型,这意味着传递的值会被复制一份。对于引用类型,这意味着传递的引用会被复制一份。对于最后一点,引用类型的值就是该引用。
那么对于类中的方法,假设未使用 ref 关键字,我们是否可以默认认为对象输入参数是按值传递的呢?
同样,在 C# 中,除非参数被标记为 outref,否则所有参数都是按值传递的。对于标记为 ref 的参数,将传递该参数的引用到方法中,现在您可以把该参数看作是一个别名。因此,当你说
void M(ref int m) { m = 10; }

int n = 123;
M(ref n);

您可以将 M 中的 m 视为 n 的别名。也就是说,mn 只是同一存储位置的两个不同名称。

这与其他情况非常不同。

string s = "Hello, world!";
string t = s;

在这种情况下,st不是指向同一存储位置的别名。它们是两个不同的变量,恰好引用同一个对象。

那么`out`关键字呢?这是否意味着它仍然是按引用传递的?

refout 之间唯一的区别是 ref 要求在传递之前初始化变量。

7
@Andrey:你错了,错了,非常错了。复制的是参考文献而不是被引用的内容。这其中有很大的区别。我无法强调这一点,但你的理解非常混淆。 - jason
1
@Andrey:现在你给我的回答点了踩,请指出我回答中的错误陈述。谢谢。 - jason
2
@Jason:点赞以抵消不应有的踩。令人惊讶的是,这种问题在SO上已经被问了很多次,发布了很多错误信息,很多好的答案被踩了,而坏的答案却被赞了。 - LukeH
2
@Jason - 好的,现在我认为我错了。你能编辑你的回答吗,这样我就可以取消我的踩赞了吗?除非你修改它,否则我无法进行操作。 - Andrey
@Andrey:感谢您回来解释您的反对票并讨论此事。我已经为您编辑了我的答案。 - jason
显示剩余7条评论

13

对象的引用将按值传递。

.NET有引用类型和值类型——类都是引用类型,结构体是值类型。您可以按值或按引用传递任何一种类型。

默认情况下,所有内容都按值传递,不同之处在于对于引用类型,将传递引用

refout关键字将导致参数按引用传递——对于值类型,这意味着您现在可以进行更改,并将反映在传递的对象中。对于引用类型,这意味着您现在可以更改引用所指向的对象。


3

一个 对象 总是通过引用传递给实际对象。因此,不会对对象执行任何副本(又称“按值”)。

正如 Oded 所指出的那样,对象的引用被复制。


2
不,对象的引用始终是按值传递的(除非您指定了一个 refout 参数)。 - LukeH
2
@LukeH 他要求的是对象本身,而不是对象的引用,对吧? - Uwe Keim
2
但是您通过其引用隐式访问对象。该引用按值传递,而不是按引用传递。有引用类型和值类型,并且有按引用传递和按值传递的方式。类型和传递方式并不相同:您可以通过引用或按值传递引用类型,也可以通过引用或按值传递值类型(默认情况下,对于引用类型和值类型都是按值传递)。 - LukeH
@Jared:你的评论中有个错误拼写。应该是“按值传递”,而不是“按引用传递”。 - LukeH
@LukeH,是的。不幸的是已经过了太长时间无法编辑。既然其他评论已经传达了信息,那就直接删除吧。 - JaredPar

1

@Supercat:这相当有趣。也许混淆在于理解为什么要通过引用类型传递引用!

将类比仅扩展到 ref 类型(我认为值类型更容易理解)

一个人可以在多张纸片上写出相同的 VIN(车辆识别号码),因此你手上所有的纸条都指向同一辆车。如果你在一张纸条上写下“油漆蓝色”,在另一张纸条上写下“油漆红色”会发生什么?这说明纸条只能包含VIN(对象地址),而所有其他信息都存储在汽车本身中。

如果你想在工作室给车涂漆,你不必发送一张纸条,你只需告诉他们VIN就行了,这只是需要知道的值 - 按值传递。你仍然保留你的纸条,他们不能更改上面的内容...因此更安全。因此,他们在自己的纸条上写下VIN-引用的副本。

另一方面,您可以向同事索取上次洗过的汽车的单据,前往前场选择一辆不是上次洗过的汽车,并在其上写下已洗车辆的新VIN码,然后将单据归还 - 按引用。使用实际单据并引用实际单据的地址(架子),以便他从那里获取单据。最好不要丢失或弄湿它...不太安全。

在所有这些混乱中,没有人谈论复制、拿走或移动实际汽车,因为这不涉及值类型。


1

.Net中参数的默认传递机制是按值传递。这对于引用类型和值类型都是正确的。在引用类型的情况下,实际上传递的是引用本身,而不是对象。

当使用refout关键字时,值确实是按引用传递的(对于值类型和引用类型都是如此)。在CLR级别上,实际上refout之间没有任何区别。 out关键字是C#的一个概念,通过标记ref参数来表示(我相信这是通过modopt完成的)。


1
理解引用类型的一个重要事情是,几乎所有对引用类型变量的操作都隐式地针对被引用的对象而不是引用本身进行。我发现将引用类型视为实例ID很有帮助。打个比方,将实例看作汽车,将引用类型看作写有汽车识别号码(VIN)的纸条。如果我将VIN复制到一张纸条上,递给店里的某个人,并说“把这个涂成蓝色”,我的真正意思是“找到这个VIN对应的汽车并将其涂成蓝色”,而不是“把这张纸条涂成蓝色”。我递给那个人的不是一辆汽车,而仅仅是一个VIN;然而,我让他涂成蓝色的是停在店里的汽车,而不是我实际递给他的纸条(或其他任何东西)。这种用法是按值传递的。
假设我想让某人买一辆车并给我VIN码。我可能会在一些纸片上写下我想要的制造商、型号、颜色等信息,并递给这个人一张纸条,让他写下VIN码。在这种情况下,我希望得到带有新VIN码的纸条。这样的用法将通过引用传递VIN码,因为这个人会在我提供的纸条上写下VIN码并把它交还给我。

0

它是按值传递的,如果您打算按引用传递它,您将使用ref参数修饰符。不确定这是否允许在构造函数中使用...


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接