.NET:通过引用传递是谎言吗?

.NET: Pass-by-reference is a lie?

提问人:iam1me 提问时间:9/6/2018 最后编辑:maccetturaiam1me 更新时间:9/6/2018 访问量:445

问:

我遇到了一个有趣的案例,其中引用传递似乎在 VB.NET 中不起作用。我在下面提供了一些示例代码供大家使用。谁能解释这种现象。这是故意的,还是语言/编译器的错误?

我在此代码中看到的是“增量后”读数与“增量前”读数相同。

Public Class Wrapper
    Public Property Value As Integer
End Class

Sub Main()

    Dim rand As New Random()

    Dim w As New Wrapper()
    w.Value = rand.Next()
    Console.WriteLine("Before Increment: {0}", w.Value)

    Try
        Increment(w.Value)
    Catch ex As Exception
    End Try

    Console.WriteLine("After Increment: {0}", w.Value)

    Console.ReadLine()
End Sub

Public Sub Increment(ByRef i As Integer)
    i += 1
    Throw New Exception()
End Sub
.NET vb.net 传递引用

评论

2赞 D-Shih 9/6/2018
这是 VB 代码而不是 C#
7赞 Eric Lippert 9/6/2018
不可以,您不能将其转换为 C# 并看到相同的结果,因为 C# 将属性作为 byrefs 传递的规则与 VB 不同。试试吧!
1赞 maccettura 9/6/2018
@iam1me,在你试图暗示埃里克·利珀特是错的之前,你可能想检查一下他的凭据
1赞 iam1me 9/6/2018
实际上,我不久前将其更正为 VB.NET 标签。当我纠正它时,人们立即开始做出反应。
3赞 Richardissimo 9/6/2018
不,你没有(检查版本历史记录)。您添加了 VB.net 标记。其他人已经删除了标记和对 C# 的引用。

答:

21赞 Eric Lippert 9/6/2018 #1

我遇到了一个有趣的案例,其中引用传递似乎在 VB.NET 中不起作用。

事实上,这是一个非常有趣的案例。

我在下面提供了一些示例代码供大家使用。谁能解释这种现象。

是的。

这是故意的,还是语言/编译器的错误?

此行为是设计使然,不是错误。你不应该像这样编写 VB 代码。如果这样做时很痛,请停止这样做

这一切都是有道理的,但你必须理解它的逻辑。遂

  • byref 是变量的别名。也就是说,当您将变量传递给采用 byref 的方法时,形式参数将成为该变量的别名。我们有一个变量有两个名称
  • 属性不是变量。属性是一对方法:getter 和 setter。属性可以由变量支持,但属性不是变量。它是产生值的 getter 和接收值的 setter。确保您清楚值和变量之间的区别。变量包含值。
  • 然后,如果尝试将属性传递给需要 byref 参数的方法,会发生什么情况?在 C# 中,这是非法的。在 VB 中,编译器会为您生成一个临时变量,并使用 copy-in-copy-out 语义通过 ref 传递该变量。也就是说,您的程序等同于:

Try
    Dim Temp As Integer
    Temp = w.Value  ' copy-in
    Increment(Temp) ' make an alias to Temp
    w.Value = Temp  ' copy-out
Catch ex As Exception
End Try

现在应该很明显为什么你的程序有这样的行为。抛出发生在复制之前


人们常说 C# 和 VB 是“同一种语言”,但语法不同,这有一定的道理。但是,显示微小差异的示例说明了这些语言具有不同的设计原则。C# 和 VB 在处理 ref 传递的值方面有所不同,这并非偶然!

C# 的设计原则包括编译器应该在代码看起来错误时告诉你,特别是编译器不应该通过猜测你的意思来“掩盖”问题,并发出代码让它在大多数时候都能工作。设计团队认为 C# 程序员的态度是“编译器是我的朋友,当我出错时告诉我,这样我就可以改进”。

VB 的设计原则包括代码可能工作得很好,如果有什么东西看起来不太对劲,弄清楚用户的意思并让它工作,即使这意味着引入不保留对象身份的代码,或者添加隐藏的复制进复制或其他什么。设计团队认为 VB 程序员的态度是“编译器经常挡住我的路;我已经表达了一个意图,所以让它发挥作用”。

这两种设计原则都是完全明智的,并且每个原则都有大量的开发人员选区。我认为Microsoft花了几十年的时间将语言开发的费用翻了一番,以便开发人员能够选择适合他们气质的语言,这真是太好了。

也就是说:在 C# 中,编译器会执行类似的操作:创建一个临时变量,为其赋值,然后通过 ref 传递变量。

挑战:创建一个程序来证明这一事实。

提示 #1:可变结构在 C# 中是一种不好的做法,这是有原因的。

提示 #2:在什么情况下,变量在 C# 中被视为值?

评论

0赞 cwharris 9/6/2018
w.Value = Temp也许应该被删除?根据您的描述,这意味着我认为这不代表 OP 的代码,因为正在更新而没有更新。Tempw.Value
0赞 Eric Lippert 9/6/2018
@cwharris:我没有听从你的观点。VB 实现复制-进-复制-出。您建议我删除副本。为什么?
1赞 Eric Lippert 9/6/2018
@cwharris:更仔细地阅读原始代码。复制永远不会到达,因为该方法会抛出,将控制权转移到捕获并跳过复制。
1赞 iam1me 9/6/2018
好答案,谢谢@EricLippert。
1赞 Eric Lippert 9/6/2018
@ParrishHusband:我没有遵循你的思路;您能解释一下为什么您认为这显示了正在创建的临时变量吗?(事实并非如此;这里发生的事情是,每次通过循环时,都会创建一个名为 b 的新变量。请记住,在逻辑上应该与for(...n times...) { int b = blah(); }int b1 = blah(); int b2 = blah(); int b3 = blah(); ... int bn = blah();