.NET:按引用传递是一个谎言?

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

我遇到了一个有趣的案例,在 VB.NET 中传递引用似乎不起作用。我在下面提供了一些示例代码供大家使用。任何人都可以解释这种现象。这是故意的,还是 language/compiler?

的错误

我在这段代码中看到的是 "After Increment" 读数与 "Before Increment" 读数相同。

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

I encountered an interesting case where pass-by-reference does not appear to work in VB.NET.

的确,这是一个很有趣的案例。

I've supplied some sample code below for you all to play with. Can anyone explain this phenomenon.

是的。

Is this intended, or a bug with the language/compiler?

此行为是设计使然,并非错误。 你不应该这样写 VB 代码。如果您这样做时感到疼痛,停止这样做

这都是有道理的,但你必须理解它的逻辑。跟随

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

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 是语法不同的 "the same language",这有一定道理。但是,显示细微差异的示例说明这些语言具有不同的设计原则。 C# 和 VB 对 ref!

传递的值的处理方式不同并非偶然

C# 的设计原则包括编译器应该在代码看起来有误时告诉您,特别是编译器不应该 "paper over" 通过猜测您的意思来解决问题,并发出代码以使其正常工作大多数时候。设计组看到C#程序员的态度是"the compiler is my friend who tells me when I'm wrong so I can improve".

VB 的设计原则包括代码可能工作得很好,如果有什么地方看起来不太正确,弄清楚用户的意思并使其工作,即使这意味着引入代码也就是说,不保留对象身份,或添加隐藏的复制输入复制输出或其他任何内容。设计团队将 VB 程序员的态度视为 "the compiler too often stands in my way; I've expressed an intention so make it work"。

这两种设计原则都是完全合理的,并且每种原则都有大量的开发人员选区。我觉得微软花了几十年的时间将语言开发的费用翻倍,让开发者有能力选择适合自己性格的语言,这真是太好了。

就是说:在 C# 中,有些情况下编译器会做类似的事情:创建一个临时变量,为其赋值,然后通过 ref 传递该变量。

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

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

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