views:

104

answers:

2

I've been working in .NET for some time now, but occasionally I still get confused by a disparity between the framework and my prior experience in C++.

In .NET, all objects are either value types or reference types. Reference types are allocated on the heap while value types are allocated on the stack (in the current CLR implementation, anyway). I get that. However, at least in VB.NET, you can still define a constructor on a value type. You can do this:

Public Structure Coordinates
    Public x As Integer
    Public y As Integer
    Public z As Integer

    Sub New(ByVal x As Integer, ByVal y As Integer, ByVal z As Integer)
        Me.x = x
        Me.y = y
        Me.z = z
    End Sub
End Structure

With this code in place, I can write Dim c As Coordinates and then access the members of c myself, or I can write Dim c As New Coordinates(10, 20, 30). What is the difference between these two approaches? Obviously the new keyword is not allocating the object on the heap since it's a value type. I'm just not clear on whether constructors on value types are merely a convenience or if they actually do something other than execute like any other method.

To perhaps make my point clearer: I could easily have removed the definition for New in the above code, and still I could do this:

Dim c1 As Coordinates
Dim c2 As New Coordinates

Do these two lines do anything different?


EDIT: Guffa has pointed out that declaring and constructing are not the same. The following code demonstrates this:

For i As Integer = 1 To 3
    Dim c1 As Coordinates
    Dim c2 As New Coordinates

    c1.x += 1
    c2.x += 1

    Console.WriteLine("c1.x = {0}, c2.x = {1}", c1.x, c2.x)
Next

' output: '
' c1.x = 1, c2.x = 1 '
' c1.x = 2, c2.x = 1 '
' c1.x = 3, c2.x = 1 '

So, in a scenario involving iteration, local variables representing value types are not re-allocated on the stack; they stay put. I took a look at this same scenario using plain old integers and found the same behavior:

For i As Integer = 1 To 3
    Dim x As Integer
    Dim y As New Integer

    x += 1
    y += 1

    Console.WriteLine("x = {0}, y = {1}", x, y)
Next

' output: '
' x = 1, y = 1 '
' x = 2, y = 1 '
' x = 3, y = 1 '
+2  A: 

There is always a default (parameterless) constructor for a struct (It's an error to declare it).

for VB.Net (EDIT)

it's the same ;), see here

for C#:

The difference between Coordinates c1; and Coordinates c2 = New Coordinates(); is that for the 1st one struct fields remain unassigned and the object can not be used until all fields are initialized. The 2nd one initialize all fields within the struct object.

For more info go here, it's well detailed.

najmeddine
Maybe there is a difference between the syntactical rules of C# versus VB.NET? You can access the members of a `Structure` in VB.NET after having only declared it. See my edit.
Dan Tao
You're right; I edited my answer to be more explicit about the difference.
najmeddine
@najmeddine: To be fair, I was pretty sure the rules were different, and I half-predicted that someone would come along and provide an answer from a C# perspective with the assumption that they were the same. It's definitely something useful to be aware of, especially for someone like me who uses both languages.
Dan Tao
and I thank you, I wasn't aware of that.
najmeddine
+3  A: 

Using the New keyword in the declaration like this:

Dim c2 As New Coordinates

is just a shortcut for:

Dim c2 As Coordinates = New Coordinates()

The parameterless constructor is automatically created for the structure, and it just returns a value with all properites set to their default value (i.e. all bytes zeroed out), functionally equivalent to:

Public Sub New()
   x = 0
   y = 0
   z = 0
End Sub

In VB all local variables are initialized when you enther the method, so the variable c1 declared without the New keyword will also be zeroed out. The difference is not visible until you execute the statement more than once:

For i As Integer = 1 to 3
   Dim c1 As Coordinates
   Dim c2 As New Coordinates
   c1.x += 1
   c2.x += 1
   Console.WriteLine("c1.x = {0}, c2.x = {1}", c1.x, c2.x)
Next

Output:

c1.x = 1, c2.x = 1
c1.x = 2, c2.x = 1
c1.x = 3, c2.x = 1

The c1 variable is initialized only once, while the c2 variable is initialized each time the statement is executed.

As a structure value can exist without a constructor being called first, the structure constructor is mostly a convenient way of initializing the structure. It's neater than a static method that creates a structure value, but it works pretty much the same:

Public Shared Function Create(ByVal x As Integer, ByVal y As Integer, ByVal z As Integer) As Coordinates
   Dim item As Coordinates
   item.x = x
   item.y = y
   item.z = z
   Return item
End Function

Dim c3 As Coordinates = Coordinates.Create(1, 2, 3)
Guffa
Well that is very interesting indeed... This is certainly an eye-opener.
Dan Tao