Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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?

What I am seeing with this code is that the "After Increment" read-out is the same as the "Before Increment" readout.

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
like image 739
iam1me Avatar asked Sep 05 '18 20:09

iam1me


People also ask

Is Passing By reference bad C#?

Passing value objects by reference is in general a bad design. There are certain scenarios it's valid for, like array position swapping for high performance sorting operations. There are very few reasons you should need this functionality. In C# the usage of the OUT keyword is generally a shortcoming in and of itself.

Is .NET pass by value or pass by reference?

In . NET Framework, all objects are by default passed by value not passed by reference either it is a Value Type (so called Primitive types like int, char, double, etc.) or Reference Type (class, interface, delegate, string, etc.).

What is a disadvantage of passing by reference?

The major disadvantages of using call by reference method are a follows: A function taking in a reference requires ensuring that the input is non-null. Thus, a null check is not supposed to be made. Thus, it has a non-null guarantee. Further, passing by reference makes the function not pure theoretically.

Does C# automatically pass by reference or value?

In C#, arguments can be passed to parameters either by value or by reference. Remember that C# types can be either reference types ( class ) or value types ( struct ): Pass by value means passing a copy of the variable to the method. Pass by reference means passing access to the variable to the method.


1 Answers

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

Indeed, this is quite an interesting case.

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

Yes.

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

This behaviour is by design and it is not a bug. You should not write VB code like this. If it hurts when you do this, stop doing it.

This all makes sense, but you have to understand the logic of it. Follow along

  • A byref is an alias to a variable. That is, when you pass a variable to a method that takes a byref, the formal parameter becomes an alias for the variable. We have one variable with two names.
  • A property is not a variable. A property is a pair of methods: a getter and a setter. A property may be backed by a variable, but a property is not a variable. It is a getter that produces a value and a setter which receives a value. Make sure you are clear on the difference between values and variables. Variables contain values.
  • What then happens if you attempt to pass a property to a method expecting a byref parameter? In C#, that's illegal. In VB, the compiler generates a temporary variable for you and passes it by ref using copy-in-copy-out semantics. That is, your program is equivalent to:

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

Now it should be obvious why your program has the behaviour that it does. The throw happens before the copy-out.


People often say that C# and VB are "the same language" with different syntax, and there is some truth to that. However, examples that show small differences illustrate that the languages have different design principles. It is not an accident that C# and VB differ in their treatment of values passed by ref!

Design principles of C# include that the compiler should tell you when the code looks wrong and in particular the compiler should not "paper over" problems by guessing what you meant, and emitting code to kinda-sorta make it work most of the time. The design team sees the attitude of C# programmers as "the compiler is my friend who tells me when I'm wrong so I can improve".

Design principles of VB include that the code probably works just fine, and that if there's something that looks not quite right, figure out what the user meant and make it work, even if that means introducing code that, say, doesn't preserve object identity, or adds a hidden copy-in-copy-out or whatever. The design teams sees the attitude of VB programmers as "the compiler too often stands in my way; I've expressed an intention so make it work".

Both design principles are entirely sensible and there are large developer constituencies for each. I think it is quite nice that Microsoft has spent decades doubling the expense of language development so that developers have the ability to choose a language that suits their temperaments.

That said: there are situations in C# where the compiler will do something similar: create a temporary variable, assign a value to it, and then pass the variable by ref.

Challenge: create a program which demonstrates this fact.

Hint #1: mutable structs are a bad practice in C# for a reason.

Hint #2: under what circumstances is a variable treated as a value in C#?

like image 132
Eric Lippert Avatar answered Sep 22 '22 15:09

Eric Lippert