【问题标题】:C# constructor, object parameter is passed by reference or valueC#构造函数,对象参数通过引用或值传递
【发布时间】:2016-02-28 12:13:32
【问题描述】:

如果你有一个类和一个将对象作为输入参数的构造函数 - 该对象是通过引用传递还是通过值传递?

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

out 关键字呢?这是否仍然意味着它是通过引用传递的?

【问题讨论】:

标签: c#


【解决方案1】:

如果您有一个类和一个将对象作为输入参数的构造函数 - 该对象是通过引用传递还是通过值传递?

所有参数在 C# 中都是按值传递的,除非参数标有outref

这是一个巨大的混乱来源。我会更明确地说明一些事情。

除非参数标有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 要求在传递之前初始化变量。

【讨论】:

  • 不正确。引用对象是通过引用传递的,否则它必须传递一个对象的克隆。 ref 的作用是允许更改引用以引用方法中的另一个对象。
  • @Andrey:你错了,错了,错了。它是引用的副本,而不是引用的副本。这是个很大的差异。我不能足够强烈地说明这一点,但是您的理解非常混乱。
  • @Andrey:现在,你否决了我的回答。请在我的回答中指出一个不正确的陈述。谢谢。
  • @Jason:赞成以抵消不应有的反对票。令人惊讶的是,这类问题在 SO 上被问了多少次,发布了多少错误信息,有多少好的答案被否决,而坏的答案被赞成。
  • @Jason - 好的,现在我想我错了。您可以编辑您的答案,以便我可以删除我的反对票吗?除非你触摸它,否则它不允许我这样做。
【解决方案2】:

对象的引用将按值传递。

.NET 有引用类型和值类型——类都是引用类型,结构都是值类型。您可以通过值或引用传递一个。

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

refout 关键字将导致参数通过引用传递 - 对于值类型,这意味着您现在可以做出将反映在传入对象中的更改。使用引用类型意味着您现在可以更改引用所指的对象。

【讨论】:

    【解决方案3】:

    object 始终通过引用传递给实际对象。因此,不会对对象执行复制(也称为“按值”)。

    正如Oded 所说,正在复制对对象的引用。

    【讨论】:

    • 不,对象的引用总是按值传递(除非您指定了refout 参数)。
    • @LukeH 他要的是对象,而不是对象的引用,不是吗?
    • 但是您通过它的引用隐式地访问该对象。该引用是按值传递的,不是按引用传递的。有引用类型和值类型,有引用传递和值传递。类型和传递它的方法不是一回事:您可以通过 ref 或按值传递 ref 类型,也可以通过 ref 或按值传递值类型(默认为 ref 和值类型都按值传递) .
    • @Jared:您的评论中有错字。大概应该说“按值传递”,而不是“按引用传递”
    • @LukeH,是的。可惜太久无法编辑。刚刚删除,因为其他 cmets 正在接收消息。
    【解决方案4】:

    .Net 中参数的默认传递机制是按值传递。对于引用类型和值类型都是如此。在引用情况下,虽然它是按值传递的实际引用,而不是对象。

    当使用refout 关键字时,该值确实是通过引用传递的(对于值和引用类型再次为真)。在 CLR 级别,refout 之间实际上没有区别。 out 关键字是一个 C# 概念,通过标记 ref 参数来表达(我相信它是通过 modopt 完成的)

    【讨论】:

      【解决方案5】:

      要理解引用类型的重要一点是,几乎所有对引用类型变量所做的事情都是隐含地针对引用所引用的事物,而不是引用本身。我发现将引用类型视为实例 ID 很有帮助。打个比方,将实例视为汽车,将引用类型视为写有汽车识别号 (VIN) 的纸条。如果我将 VIN 复制到一张纸条上,交给店里的某个人,然后说“把这个涂成蓝色”,我真正的意思是“找到有这个 VIN 的汽车,把它涂成蓝色”,而不是“把这张纸条涂成蓝色”纸蓝色”。我交给这个人的不是汽车,而只是一个 VIN;然而,我告诉他把车漆成蓝色的是坐在商店里的汽车,而不是我实际递给他的那张纸(或其他任何东西)。这样的用法就是按值传递。

      但是,假设我想要有人买一辆车并给我 VIN。我可能会在一些纸条上写下我想要的品牌、型号、颜色等,并递给对方一张纸条,在上面写上 VIN。在这种情况下,我想取回上面有新 VIN 的纸条。这种用法是通过引用传递 VIN,因为此人会将 VIN 写在我提供的一张纸上并将其还给我。

      【讨论】:

        【解决方案6】:

        @Supercat:这很有趣。也许困惑在于理解为什么要通过引用传递引用类型!

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

        一个人可能会在多张纸条上写下相同的 VIN(车辆 ID 号),因此您手上的所有纸条都指的是同一辆车。如果你在一张纸上写“涂蓝”,在另一张纸上写“涂红”怎么办?这表明单据只能包含 VIN(对象地址),所有其他信息都存储在汽车本身中。

        如果您有兴趣在车间为汽车涂漆,您不必发送单据,您只需告诉他们 VIN...只需要知道,value-pass by val。您仍然保留您的单据,他们无法更改您单据上写的内容……因此更安全。因此,他们在自己的单据上写下 VIN - 参考副本。

        另一方面,你可以让同事从货架上拿到最后洗车的单据,去前院选择一辆不是最后洗车的车,然后用洗车的新 VIN 返还单据写在上面 - 由参考。使用实际单据,并且您参考了实际单据(货架)的地址,以便他从那里获取单据。他最好不要弄丢或弄湿它……不太安全。

        在所有这些讨论中,没有人谈论复制、拿走或移动实际的汽车,因为这不是指值类型。

        【讨论】:

          【解决方案7】:

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

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2017-09-11
            • 2016-02-05
            • 1970-01-01
            • 2018-08-17
            • 1970-01-01
            • 2017-04-19
            • 1970-01-01
            相关资源
            最近更新 更多