Which classes are automatically passed by ref and which are not?

Ok, I'll bite. Jabe, your code is not demonstrating how objects are passed per se, it's merely demonstrating out parameters. You can change the original class passed in in the latter procedure because you're passing byref, but either way you're creating a new instance so it doesn't prove much.

Take the code below, which is a cut down version of your. No matter whether the instance is passed byval or byref, it's a class so you're only passing a reference and the number will always be changed.

Visual Basic:
Public Class Tester
    Public Shared Sub Main()
        Dim t As New Tester, referenceType As New MyRefType
        referenceType.RefTypeProp = 1

        MessageBox.Show("Starting Value: " & referenceType.RefTypeProp.ToString())
        t.PassByVal(referenceType)
        MessageBox.Show("After being passed by value: " & referenceType.RefTypeProp.ToString())

        t.PassByRef(referenceType)
        MessageBox.Show("After being passed by reference: " & referenceType.RefTypeProp.ToString())

        MessageBox.Show("It's always being changed because you're only passing a reference of the same object, NOT a copy of the object. Q.E.D.")
    End Sub

    Public Sub PassByVal(ByVal refType As MyRefType)
        refType.RefTypeProp = 1000
    End Sub

    Public Sub PassByRef(ByRef reftype As MyRefType)
        reftype.RefTypeProp = 9999
    End Sub
End Class

Public Class MyRefType
    Private m_intRefTypeProp As Integer
    Public Property RefTypeProp() As Integer
        Get
            Return m_intRefTypeProp
        End Get
        Set(ByVal Value As Integer)
            m_intRefTypeProp = Value
        End Set
    End Property
End Class
 
> your code is not demonstrating how objects are passed per se,
Correct, divil, because that is not the intention of the code. That is why my first post opened up w/:

In VB.NET, parameters are passed ByVal by default (i.e., when you specify no ByVal/ByRef modifier).

I also posted

When you pass ref types, it's true that a reference is passed, not the object itself. I think it's not the same w/ the notion of the "pass-by-reference" term as commonly used in the terminology of programming languages.

because I was getting the impression that there's a brewing confusion in the discussion between objects passed per se and objects passed as parameters. Usually, the terms pass-by-ref and pass-by-val are associated to the discussion of function parameter-passing.
 
JABE:

Pass by reference is the correct term for objects. When you pass an object to a parameter, it passes the pointer to the value of the object. The parameter now "references" the value via a pointer. What else would you call it, pass by pointer? That's not common terminology.
 
I hate to jump in so late, but saying that non-primitive types (classes, not structs) are always passed by ref is misleading. While it's true you can change the contents of a class when passed to a function, you can't change the original pointer to the class inside of the function. Meaning, a class variable (pointer) is passed by value - but just the pointer, not the contents of the class.

So, if you have the following:
C#:
// First, a function
private void Test(Class1 c)
{
    c = null;
}

Class1 c1 = new Class1();
// Call a function - pass by value
Test(c1);
// c1 is NOT null and the contents are still intact

If the function Test were to modify any member variables in Class1, they WOULD show up as changed outside of Test() since only the pointer (c1) is passed by value.

Structs are copied when passed to a function (unless ref is added to the parameter) so changing a value inside the function only changes that local copy. Of course, if the parameter is a class, you'll again be changing the actual class's contents.

-Nerseus
 
Nerseus:
I think your example is exactly why there's confusion on the topic. People do what you did above, or sometimes rather do c = new Class1(); and expect it to effect the original variable outside the method. I made this assumption and mistake once with strings.

Despite this, I'd still view it as passing by reference. ALL objects have their pointers passed, not the values themselves, hence passed by reference. I know it's a thin line here, but what else would you call it, pass by pointer? It's certainly not pass by value.
 
To really understand what happens, you have to know how memory is used (including pointers and the actual data structures they point to). For most people, that's WAY too much knowledge and just hearing that all non-primitive types are passed byref is good enough. It only comes to byte :) you later, when something isn't quite working as expected and you can't quite figure it out.

As long as you understand what's happening and can accurately predict what's going to happen when you call a function, you can call it By Val, By Ref, or Sally Sue Sea Shells. :)

-Nerseus
 
While it's true you can change the contents of a class when passed to a function, you can't change the original pointer to the class inside of the function.

You can actually change the original pointer to the class inside the function if ByRef in VB.NET or ref in C# was used to define the parameter in the method signature. This is what my original code in an earlier post in this thread was supposed to demonstrate.
 
Everything is passed byval by default, even objects. When you pass an object though, you are actually passing a pointer to that object. You can pass the pointer byref or byval. Either way, it will point to the same object so that it is almost like you are passing byref either way. With byval, though, you cant change what the variable points to because you would just be changing a copy of the pointer, not the origional. If you pass by ref, you can change what the variable points to because your dealing with the origional pointer, not a copy of it. There is a difference between passing objects byref and byval, and byval is the default.
 
In C#, everything is passed by value, unless either ref or out is specified on the parameter. For example, an int/Int32 is passed by value; a TrackBar reference is also passed by value. Read that last sentence again: the reference itself is passed by value! Think about the following method:

void f(TrackBar tb) { ... }

The parameter tb is passed by value. You can see this, as there is absolutely no way for tb to be altered on return to the caller: tb will definitely be referencing the same object (or be null) both before and after the call to f(). Of course, the object to which tb refers can be modified in any way you like. This does not mean that the tb reference itself is being passed by reference! Compare this to the following:

void f(ref TrackBar tb) { ... }

Now it is possible that tb could be altered, in the eyes of the caller, during the call to f(). That is, tb is being passed by reference. For example, tb could be referring to a TrackBar instance before the call to f(), but be null by the time the method returns.

The moral of the story is this: everything (in C#) is passed by value, unless explicitly stated otherwise. Don't be confused between true pass-by-reference (the parameter is marked either ref or out) and simply passing an object reference by value.
 
SOOoooo. Who is the smartest ? :)


Now my 2c:
For a VB programmer, there is not much of a difference between passing an object byval or byref. Because in either case it is possible for the sub to change the contents of the object.

That's what counts for me.

pointers are just vehicles.
objects are the precious goods.
 
I was curious about all this and decided to try it out. Image and Bitmap are classes. MB is a form that shows the picture.

C#:
Bitmap test= (Bitmap)Image.FromFile("D:\\copy.bmp");
			a.MB.ShowDialog(test);
			testFunk(test);
			a.MB.ShowDialog(test); //The picture remains the same.
		}

		void testFunk(Bitmap b)
		{
			b= (Bitmap)Image.FromFile("D:\\pic.tif");
		}

//------------------Example 2----------------------------------

		Bitmap test= (Bitmap)Image.FromFile("D:\\copy.bmp");
			a.MB.ShowDialog(test);
			testFunk(ref test);
			a.MB.ShowDialog(test); //The picture is changed.
		}

		void testFunk(ref Bitmap b)
		{
			b= (Bitmap)Image.FromFile("D:\\pic.tif");
		}
Is C++ different than C# in this manner?

For example, when you pass an array in C++ it is always passed by reference and does not need the & (reference symbol in C++). I am pretty sure that the original data will always be changed. But in C#, looking at the example above, it does not seem to work that way.
 
If you keep in mind what's going on, you'll know what to expect.

Reference the image below. The list of blocks at the top represent the memory occupied by the Bitmap object. Keep in mind that this is a class not a struct. The variable test is a separate piece of memory that points to the Bitmap object.

Since you defined the parameter b in the function without the "ref" keyword, the parameter will be passed by val. That means that the memory occupied by the variable is copied and sent to the subroutine.

If you look at the top portion, you'll see that I've shown "b" as a separate memory chunk that points to the same underlying data. That means when you assign b to a new Bitmap, you're only repointing that variable b's memory to a new chunk of memory (similar to the bottom portion of the picture).

If you changed that declaration to use "ref b" instead, then what you would be sending into the subroutine is NOT a copy of the pointer, but the actual pointer "test". That means modifying b would be modifying test, which would mean losing your old bitmap.

In either case, if you were to change the contents of the Bitmap (using b), such as creating a Graphics object and doing some drawing, you would affect the picture. It may appear that you've copied by reference, but it wouldn't really be true.


Now for structs.

Structs work just like basic datatypes (Date, String, Int, etc.) in that they are truly copied by value. In the bottom portion of the picture I've drawn what happens when you pass a struct to a function.

Instead of copying the pointer (but the pointer still points to the same memory as the original variable), the entire chunk of data is copied.

This means modifying b's data will not modify test's data. In the example above for class's, if you modify the value of a field in a struct using b.FieldA = new value, it will NOT affect the value in the variable test.

A final note on struct's memory.
Technically, the bottom of the picture isn't accurate as there really isn't a separate chunk of memory that points to a struct. The variable really represents the place in memory where the struct's data begins, rather than using a pointer to the real chunk of data as with a class.

So yes, even in C# and VB it helps to know about pointers and memory (at least as far as understanding what's going on when you declare and use objects).

-nerseus
 

Attachments

I don't think I've ever seen the term "by reference" scewed in so many ways.

*sighs and goes off to watching Jay Leno*
 
It's the same in C++

aewarnick said:
Is C++ different than C# in this manner?

No, it's exactly the same thing. For example, in C#, you could have something like this:

C#:
using System;

class A { }
class B : A { }
class C : A { }

class Test
{
    static void byval(A a)
    {
        a = new C();
    }

    static void byref(ref A a)
    {
        a = new C();
    }

    static void Main()
    {
        A a = new B();
        Console.Write(a + " => ");
        byval(a);
        Console.WriteLine(a);

        a = new B();
        Console.Write(a + " => ");
        byref(ref a);
        Console.WriteLine(a);
    }
}

The corresponding C++ [is there a tag for including C++?] would look something like this:

C#:
#include <iostream>

class A { };
class B : public A { };
class C : public A { };

void byval(A* a)
{
    a = new C();
}

void byref(A*& a)
{
    a = new C();
}

int main()
{
    A* a = new B();
    std::cout << a << " => ";
    byval(a);
    std::cout << a << std::endl;

    a = new B();
    std::cout << a << " => ";
    byref(a);
    std::cout << a << std::endl;
}

The results are, for C#:

B => B
B => C

and, on my computer, for C++:

00312C50 => 00312C50
00312C70 => 00312C80
 
aewarnick said:
When I don't use ref when sending a class I am not losing any performance am I?

No. I would suggest that you only use ref when you really need the argument to be passed by reference. That is, you need that the called method should be able to alter the value of the parameter itself.
 
Back
Top