How to assign a value to a variable of type Double, that has been passed as Object? - vb.net

I am trying to assign a value to global variable, which has a Property of type Double. This Property is passed as Object and the assignment fails.
In the example code below, the value is never assigned to the actual object, but only locally:
Public Class Form1
Friend Home As New Building
Private Sub AssignValues() Handles Me.Load
'Objects of different types are added to a list
Dim listObjects As New List(Of Object)
listObjects.Add(Home.Surface)
'All the Objects in listObjects are assigned a value that
'is stored as String
For Each o As Object In listObjects
SetProperty(o, "45.6")
Debug.Print("Surface = " & Home.Surface.ToString)
Next
End Sub
Private Sub SetProperty(ByRef Variable As Object, ByVal Value As String)
Select Case Variable.GetType
Case GetType(Double)
Variable = CDbl(Value)
Case Else
'...
End Select
End Sub
End Class
Public Class Building
Dim _surface As Double = 0
Public Property Surface As Double
Get
Return _surface
End Get
Set(ByVal value As Double)
_surface = value
End Set
End Property
End Class
The program invariably outputs Surface = 0 instead of 45.6. What am I doing wrong?
I tried to pass the Variable as reference, as suggested here, but without success. I also read about using Reflection, but there ought to be something simpler than that...

When your adding home.surface to the list, your adding a copy of the double to the list and then adjusting that copy. Stick a watch on "o" and see how it changes whilst home.surface remains the same.
If you want to use reflection, try something along these lines.
Dim prop As Reflection.PropertyInfo = o.GetType().GetProperty("Surface")
prop.SetValue(o, 45.6)

With Variable.GetType you will get always Object, because this is the type of Variable. What you can do with an Object is converting/casting it into a different type (like Double).
The best way to determine the "original type" from where the Object comes would be including an additional variable telling it. Another option might be converting the given Object into the target Type and see if it is not nothing/does not trigger an error. But this second option is not too accurate, mainly when dealing with "equivalent types" like Doubles/Integers.

Related

Type mismatch trying to set data in an object in a collection

I am getting Runtime Error 13 when trying to update an object stored in a collection. Here is a minimal example.
The class (Class2) of the objects to be stored in the collection.
Option Explicit
Private pHasA As Boolean
Private pHasB As Boolean
Private pSomeRandomID As String
Property Get HasA() As Boolean
HasA = pHasA
End Property
Property Get HasB() As Boolean
HasB = pHasB
End Property
Property Let HasA(propValue As Boolean)
pHasA = propValue
End Property
Property Let HasB(propValue As Boolean)
pHasB = propValue
End Property
Property Let RandomID(propValue As String)
pSomeRandomID = propValue
End Property
Sub SetHasValues(key As String)
Select Case key
Case "A"
pHasA = True
Case "B"
pHasB = True
End Select
End Sub
Minimal code that reproduces the error:
Option Explicit
Private Sub TestCollectionError()
Dim classArray As Variant
Dim classCollection As Collection
Dim singleClass2Item As Class2
Dim iterator As Long
classArray = Array("A", "B", "C")
Set classCollection = New Collection
For iterator = LBound(classArray) To UBound(classArray)
Set singleClass2Item = New Class2
singleClass2Item.RandomID = classArray(iterator)
classCollection.Add singleClass2Item, classArray(iterator)
Next iterator
Debug.Print "Count: " & classCollection.Count
singleClass2Item.SetHasValues "A" ' <-- This code works fine.
Debug.Print "New Truth values: " & singleClass2Item.HasA, singleClass2Item.HasB
For iterator = LBound(classArray) To UBound(classArray)
classCollection(classArray(iterator)).RandomID = classArray(iterator)
classCollection(classArray(iterator)).SetHasValues classArray(iterator) '<-- Type mismatch on this line.
Next iterator
'***** outputs
'''Count: 3
'''New Truth values: True False
' Error dialog as noted in the comment above
End Sub
While the code above appears a little contrived, it is based on some real code that I am using to automate Excel.
I have searched for answers here (including the following posts), but they do not address the simple and non-ambiguous example that I have here. The answers that I have found have addressed true type mismatches, wrong use of indexing or similar clear answers.
Retrieve items in collection (Excel, VBA)
Can't access object from collection
Nested collections, access elements type mismatch
This is caused by the fact, that the parameter of your procedure SetHasValues is implicitely defined ByRef.
Defining it ByVal will fix your problem.
#ADJ That's annoying, but perhaps the example below will allow you to start making a case for allowing RubberDuck.
I've upgraded your code using ideas and concepts I've gained from the rubberduck blogs. The code now compiles cleanly and is (imho) is less cluttered due to fewer lookups.
Key points to note are
Not relying on implicit type conversions
Assigning objects retrieved from collections to a variable of the type you are retrieving to get access to intellisense for the object
VBA objects with true constructors (the Create and Self functions in class2)
Encapsulation of the backing variables for class properties to give consistent (and simple) naming coupled with intellisense.
The code below does contain Rubberduck Annotations (comments starting '#)
Updated Class 2
Option Explicit
'#Folder("StackOverflowExamples")
'#PredeclaredId
Private Type Properties
HasA As Boolean
HasB As Boolean
SomeRandomID As String
End Type
Private p As Properties
Property Get HasA() As Boolean
HasA = p.HasA
End Property
Property Get HasB() As Boolean
HasB = p.HasB
End Property
Property Let HasA(propValue As Boolean)
p.HasA = propValue
End Property
Property Let HasB(propValue As Boolean)
p.HasB = propValue
End Property
Property Let RandomID(propValue As String)
p.SomeRandomID = propValue
End Property
Sub SetHasValues(key As String)
Select Case key
Case "A"
p.HasA = True
Case "B"
p.HasB = True
End Select
End Sub
Public Function Create(ByVal arg As String) As Class2
With New Class2
Set Create = .Self(arg)
End With
End Function
Public Function Self(ByVal arg As String) As Class2
p.SomeRandomID = arg
Set Self = Me
End Function
Updated test code
Private Sub TestCollectionError()
Dim classArray As Variant
Dim classCollection As Collection
Dim singleClass2Item As Class2
Dim my_item As Variant
Dim my_retrieved_item As Class2
classArray = Array("A", "B", "C")
Set classCollection = New Collection
For Each my_item In classArray
classCollection.Add Item:=Class2.Create(my_item), key:=my_item
Next
Debug.Print "Count: " & classCollection.Count
Set singleClass2Item = classCollection.Item(classCollection.Count)
Debug.Print "Initial Truth values: " & singleClass2Item.HasA, singleClass2Item.HasB
singleClass2Item.SetHasValues "A" ' <-- This code works fine.
Debug.Print "New Truth values: " & singleClass2Item.HasA, singleClass2Item.HasB
For Each my_item In classArray
Set my_retrieved_item = classCollection.Item(my_item)
my_retrieved_item.RandomID = CStr(my_item)
my_retrieved_item.SetHasValues CStr(my_item)
Next
End Sub
The 'Private Type Properties' idea comes from a Rubberduck article encapsulating class variable in a 'This' type. My take on this idea is to use two type variable p and s (Properties and State) where p holds the backing variables to properties and s hold variables which represent the internal state of the class. Its not been necessary to use the 'Private Type State' definition in the code above.
VBA classes with constructors relies on the PredeclaredID attribute being set to True. You can do this manually by removing and saving the code, using a text editor to set the attributer to 'True' and then reimporting. The RUbberDuck attribute '#PredeclaredId' allows this to be done automatically by the RubberDuck addin. IN my own code the initialiser for class2 would detect report an error as New should not be used when Classes are their own factories.
BY assigning and intermediate variable when retrieving an object from a class (or even a variant) you give Option Explicit the best change for letting you n=know of any errors.
An finally the Rubberduck Code Inspection shows there are still some issues which need attention

Why does the Default Property access the setter when removing an item

In the code below, when trying to remove an item from the Cases list the code breaks in the Setter with an index out of bounds. When running the debugger in VisualStudio 2017 it successfully goes through the Remove() function and deletes the last item but after returning to Main() it will break on the Setter and the call stack says it is coming from the Remove call. Example code below:
Sub Main()
Dim Cases As Collection = New Collection()
Dim caseIndex As Integer = 2
Cases.Remove(Cases(caseIndex))
End Sub
Public Class Collection
Public WithEvents Cases As List(Of CaseClass)
Public Sub New()
Cases = New List(Of CaseClass)()
Cases.Add(New CaseClass)
Cases.Add(New CaseClass)
Cases.Add(New CaseClass)
End Sub
Default Public Property BeltCase(ByVal Index As Integer) As CaseClass
Get
Return Cases(Index)
End Get
Set(ByVal Value As CaseClass)
Cases(Index) = Value
End Set
End Property
Public Sub Remove(ByRef BeltCase As CaseClass)
Cases.Remove(BeltCase)
End Sub
End Class
Public Class CaseClass
Public test As Int16
End Class
Call Stack:
TestingVBBug.exe!TestingVBBug.Module1.Collection.set_BeltCase(Integer Index,TestingVBBug.Module1.CaseClass Value) Line 25 Basic
TestingVBBug.exe!TestingVBBug.Module1.Main() Line 6 Basic
So why would we be going through the Setter at all. And why does that happen after we exit the remove function?
The problem is caused by your Remove() method, that is, you have a ByRef parameter (for some reason). When you use ByRef, any changes made to the parameter inside the method must be reflected to the variable that was passed to the method. That happens by reassigning the value to the original variable.
In your case, it works like this:
The Remove() method is called and a variable (Cases(caseIndex)) is passed to it.
Some work is done inside the Remove() method which might, or might not include changing the value of the parameter BeltCase.
The value of the parameter BeltCase gets reassigned to the variable that was originally passed to the method (which is Cases(caseIndex)).
As a result of the above step, the setter of the BeltCase property gets called with Index = 2 which raises the out of range exception because Cases(2) doesn't exist (was removed).
To confirm, you can see this problem go away when you replace this line:
Cases.Remove(Cases(caseIndex))
..with:
Dim myCase As CaseClass = Cases(caseIndex)
Cases.Remove(myCase)
That way, you create a new variable which refers to the same CaseClass object and most importantly avoid calling the setter of your Collection.BeltClase property.
However, a better solution would be to not use ByRef in the first place since you don't seem to need it in this situation. So, simply use Public Sub Remove(ByVal BeltCase As CaseClass) instead.
Check this question for more about ByVal and ByRef with objects.
One last thing, please don't call your class Collection because it can be very confusing to anyone looking at your project.

How To Iterate Over Members of a Structure Array in VB.net Using Member Function

I have a vb.net enumeration that looks like this:
' define enumeration for keypad states
Enum KeyPadState
KEYPAD_NO ' no keypad
KEYPAD_UC ' upper case keypad
KEYPAD_LC ' lower case keypad
KEYPAD_NU ' numeric keypad
KEYPAD_SY ' symbol keypad
End Enum
I then defined a structure element to be used to translate the members of the above enumeration from enumeration values to string values and back again. The declared structure looks like below. Note the member functions that I have tried to insert. The "New" one is working.
' define keypad type look up structure
Private Structure KeyPadXlat
Dim KeyPadEnum As KeyPadState
Dim KeyPadStr As String
' initializer subroutine
Public Sub New(nKeyPadEnum As KeyPadState, nKeyPadStr As String)
KeyPadEnum = nKeyPadEnum
KeyPadStr = nKeyPadStr
End Sub
' translate string to enum
Public Function ToEnum(xKeyPadStr As String) As KeyPadState
For Each item As KeyPadXlat In ????
Next
End Function
' translate enum to string
Public Function ToStr(xKeyPadEnum As KeyPadState) As String
End Function
End Structure
The actual instance of the structure array is shown below with its initializer code.
Dim KeyPadLookUp() As KeyPadXlat = { _
New KeyPadXlat(KeyPadState.KEYPAD_NO, "KEYPAD_NO"), _
New KeyPadXlat(KeyPadState.KEYPAD_UC, "KEYPAD_UC"), _
New KeyPadXlat(KeyPadState.KEYPAD_LC, "KEYPAD_LC"), _
New KeyPadXlat(KeyPadState.KEYPAD_NU, "KEYPAD_NU"), _
New KeyPadXlat(KeyPadState.KEYPAD_SY, "KEYPAD_SY") _
}
So my question is with regard to the member functions I am trying to create to translate back and forth between the enumeration value and the string value. I have copied one of them here again for reference:
' translate string to enum
Public Function ToEnum(xKeyPadStr As String) As KeyPadState
For Each item As KeyPadXlat In ????
Next
End Function
What I need help with is how to write the code for the For Each loop so that it iterates across all of the elements of the structure array when being in a member function.
To be honest, you really don't need all that code. This should do it nicely.
Enum KeyPadState
KEYPAD_NO ' no keypad
KEYPAD_UC ' upper case keypad
KEYPAD_LC ' lower case keypad
KEYPAD_NU ' numeric keypad
KEYPAD_SY ' symbol keypad
End Enum
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim state As KeyPadState
state = KeyPadState.KEYPAD_LC
'this line will assign the name of the enum `state` to a string called `tempstring`
'It's hardly worth encapsulating it into a function so I've left it as is
'But if you want to provide consistent code, it would be better to.
Dim tempstring As String
tempstring = [Enum].GetName(GetType(KeyPadState), state)
Dim anyString As String = "KEYPAD_UC"
Dim tempState As KeyPadState
'the following line will try to parse `anyString` to an enum value of the same type as the variable to be assigned.
'In this case `state`
tempState = ParseToKeypadState(anyString)
End Sub
Private Function ParseToKeypadState(tempString As String) As KeyPadState
Dim returnValue As KeyPadState
If Not [Enum].TryParse(tempString, returnValue) Then
'handle parsing error here
End If
Return returnValue
End Function
There's all sorts wrong with your code there.
Firstly, I'd suggest that your naming conventions are poor. If you do as is done throughout the .NET Framework then you enumeration would look like this:
Enum KeyPadState
None
UpperCase
LowerCase
Numeric
Symbol
End Enum
That's clear and self-documenting.
Secondly, it is not recommended to use abbreviations like "Xlat". That's meaningless to anyone without prior knowledge. Is it so onerous to write "Translate" and then let Intellisense find it whenever you need to use it in code?
As for the implementation of your class, why does it need any methods at all? You are passing in the KeyPadState value and the text representation when you create an instance so what is there for those methods to do? Your structure should simply be a constructor and two properties:
Private Structure KeyPadStateTranslation
Public ReadOnly Property Value As KeyPadState
Public ReadOnly Property Text As String
Public Sub New(value As KeyPadState, text As String)
Me.Value = value
Me.Text = text
End Sub
End Structure
The property values are set when the instance is created and they are retrieved via the properties. Everything also has an appropriate name.
That said, you don't even need to provide the text because you can simply call ToString on the value to get it:
Private Structure KeyPadStateTranslation
Public ReadOnly Property Value As KeyPadState
Public ReadOnly Property Text As String
Public Sub New(value As KeyPadState)
Me.Value = value
Me.Text = value.ToString()
End Sub
End Structure
Also, the fact that that structure is declared Private is an issue. That indicates that it is declared inside another type. That's not right. Structures are first-class types, just like classes, so they belong in their own dedicated code file, just like classes.
What's the point of that structure at all though? You'd still have to loop through your array to find an instance that matches either a value or some text so it doesn't really help. A Dictionary might be better but you may as well just call ToString on a value if you need to convert that way and use Enum.Parse or .TryParse when you need to go the other way.

vb.net Loop through public structure and pass to generics

I have inherited some god awful vb.net code and having trouble trying to work out the generics for looping through the current structure in place.
here is a snippet of the struct
Public Structure ApplicationDetails
Dim ID As Integer
Dim AgentID As Integer
Dim ApplicationDate As Date
Dim CompletedDate As Date
here is the madness to populate it
With ApplicationInfo
.ID = If(Not IsDBNull(DT(0)("ID")), DT(0)("ID"), Nothing)
.ApplicationDate = If(Not IsDBNull(DT(0)("ApplicationDate")), DT(0)("MortgageAmount"), Nothing)
.CompletedDate = If(Not IsDBNull(DT(0)("CompleteDate")), DT(0)("MortgageAmount"), Nothing)
now i want to do something like this:
For Each item In ApplicationInfo.GetType().GetProperties()
Dim thisType = item.GetType()
Dim name = item.Name
Dim value = DtItem(Of item.GetType())(0, name.ToString(), DT)
item.SetValue(item, value, Nothing)
Next
Private Function DtItem(Of T)(ByVal num As Integer, ByVal name As String, ByRef DT As DataTable) As T
Return If(Not IsDBNull(DT(num)(name)), DT(num)(name), Nothing)
End Function
but i am not sure on the syntax to set the value and when trying to get the type i get item.GetTYpe() is not declared. I know i must be on the right track, just missing a little something.
A number of issues.
First and foremost, SetValue only works on object types, not values, so you would have to change your Structure to a Class.
Public Class ApplicationDetails
End Class
Next, you are looping through properties but your "structure" only had fields. So you need to add properties:
Public Class ApplicationDetails
Private _ID As Integer
Property ID As Integer
Get
Return _ID
End Get
Set(ByVal value As Integer)
_ID = value
End Set
End Property
//' etc
End Class
Otherwise, you would have to work with GetFields.
I don't think Generics will work here since you are only dealing with objects and you don't know the type (despite reflection):
Private Function DtItem(ByVal num As Integer, ByVal name As String, ByRef DT As DataTable) As Object
Return If(Not IsDBNull(DT(num)(name)), DT(num)(name), Nothing)
End Function
Lastly, your reflection call is wrong. Try changing it to this:
For Each item As PropertyInfo In ApplicationInfo.GetType().GetProperties
Dim value As Object = DtItem(0, item.Name, _dt)
If item.CanWrite Then
item.SetValue(ApplicationInfo, value, Nothing)
End If
Next
I'm not sure doing this through reflection is gaining you anything. In your "madness" example, it looks like you might be trying to put in a MortgageAmount, which I assume is a decimal, into a date field. That might be needed to look at.
I think you need to rethink your functional decomposition there. You're not getting much out of moving one line into a function, and I don't think you'll be able to pass a type constructor to a generic the way you are attempting to do so. You're also confusing some of your type checks.
Try something along these lines instead:
For Each item In ApplicationInfo.GetType().GetProperties()
Dim theName As String = item.Name,
isNullable = Not item.PropertyType.IsValueType OrElse _
Nullable.GetUnderlyingType(item.PropertyType) IsNot Nothing
If item.CanWrite Then
If Not IsDBNull(DT(0)(theName))
item.SetValue(ApplicationInfo, DT(0)(theName), Nothing)
ElseIf isNullable
item.SetValue(ApplicationInfo, Nothing, Nothing)
End If
End If
Next
If your code is executed immediately after the object is initialized, the isNullable checks are extraneous and you could simply take no action as the properties will be initialized as null. Otherwise, I'd recommend performing the check so you don't attempt to assign Nothing to a value type, which is going to throw an exception. Alternatively, you could modify your structure to use nullable reference types, i.e.:
Public Structure ApplicationDetails
Property ID As Integer?
Property AgentID As Integer?
Property ApplicationDate As Date?
Property CompletedDate As Date?
End Structure
EDIT
As LarsTech points out, your structure members are not properties and this will not function as anticipated unless you modify your structure to indicate these fields are in fact properties, for which the compiler would automatically generate getters and setters.

ByRef vs ByVal Clarification

I'm just starting on a class to handle client connections to a TCP server. Here is the code I've written thus far:
Imports System.Net.Sockets
Imports System.Net
Public Class Client
Private _Socket As Socket
Public Property Socket As Socket
Get
Return _Socket
End Get
Set(ByVal value As Socket)
_Socket = value
End Set
End Property
Public Enum State
RequestHeader ''#Waiting for, or in the process of receiving, the request header
ResponseHeader ''#Sending the response header
Stream ''#Setup is complete, sending regular stream
End Enum
Public Sub New()
End Sub
Public Sub New(ByRef Socket As Socket)
Me._Socket = Socket
End Sub
End Class
So, on my overloaded constructor, I am accepting a reference to an instance of a System.Net.Sockets.Socket, yes?
Now, on my Socket property, when setting the value, it is required to be ByVal. It is my understanding that the instance in memory is copied, and this new instance is passed to value, and my code sets _Socket to reference this instance in memory. Yes?
If this is true, then I can't see why I would want to use properties for anything but native types. I'd imagine there can be quite a performance hit if copying class instances with lots of members. Also, for this code in particular, I'd imagine a copied socket instance wouldn't really work, but I haven't tested it yet.
Anyway, if you could either confirm my understanding, or explain the flaws in my foggy logic, I would greatly appreciate it.
I think you're confusing the concept of references vs. value types and ByVal vs. ByRef. Even though their names are a bit misleading, they are orthogonal issues.
ByVal in VB.NET means that a copy of the provided value will be sent to the function. For value types (Integer, Single, etc.) this will provide a shallow copy of the value. With larger types this can be inefficient. For reference types though (String, class instances) a copy of the reference is passed. Because a copy is passed in mutations to the parameter via = it won't be visible to the calling function.
ByRef in VB.NET means that a reference to the original value will be sent to the function (1). It's almost like the original value is being directly used within the function. Operations like = will affect the original value and be immediately visible in the calling function.
Socket is a reference type (read class) and hence passing it with ByVal is cheap. Even though it does perform a copy it's a copy of the reference, not a copy of the instance.
(1) This is not 100% true though because VB.NET actually supports several kinds of ByRef at the callsite. For more details, see the blog entry The many cases of ByRef
Remember that ByVal still passes references. The difference is that you get a copy of the reference.
So, on my overloaded constructor, I am accepting a reference to an instance of a System.Net.Sockets.Socket, yes?
Yes, but the same would be true if you asked for it ByVal instead. The difference is that with ByVal you get a copy of the reference — you have new variable. With ByRef, it's the same variable.
It is my understanding that the instance in memory is copied
Nope. Only the reference is copied. Therefore, you're still working with the same instance.
Here's a code example that explains it more clearly:
Public Class Foo
Public Property Bar As String
Public Sub New(ByVal Bar As String)
Me.Bar = Bar
End Sub
End Class
Public Sub RefTest(ByRef Baz As Foo)
Baz.Bar = "Foo"
Baz = new Foo("replaced")
End Sub
Public Sub ValTest(ByVal Baz As Foo)
Baz.Bar = "Foo"
Baz = new Foo("replaced")
End Sub
Dim MyFoo As New Foo("-")
RefTest(MyFoo)
Console.WriteLine(MyFoo.Bar) ''# outputs replaced
ValTest(MyFoo)
Console.WriteLine(MyFoo.Bar) ''# outputs Foo
My understanding has always been that the ByVal/ByRef decision really matters most for value types (on the stack). ByVal/ByRef makes very little difference at all for reference types (on the heap) UNLESS that reference type is immutable like System.String. For mutable objects, it doesn't matter if you pass an object ByRef or ByVal, if you modify it in the method the calling function will see the modifications.
Socket is mutable, so you can pass any which way you want, but if you don't want to keep modifications to the object you need to make a deep copy yourself.
Module Module1
Sub Main()
Dim i As Integer = 10
Console.WriteLine("initial value of int {0}:", i)
ByValInt(i)
Console.WriteLine("after byval value of int {0}:", i)
ByRefInt(i)
Console.WriteLine("after byref value of int {0}:", i)
Dim s As String = "hello"
Console.WriteLine("initial value of str {0}:", s)
ByValString(s)
Console.WriteLine("after byval value of str {0}:", s)
ByRefString(s)
Console.WriteLine("after byref value of str {0}:", s)
Dim sb As New System.Text.StringBuilder("hi")
Console.WriteLine("initial value of string builder {0}:", sb)
ByValStringBuilder(sb)
Console.WriteLine("after byval value of string builder {0}:", sb)
ByRefStringBuilder(sb)
Console.WriteLine("after byref value of string builder {0}:", sb)
Console.WriteLine("Done...")
Console.ReadKey(True)
End Sub
Sub ByValInt(ByVal value As Integer)
value += 1
End Sub
Sub ByRefInt(ByRef value As Integer)
value += 1
End Sub
Sub ByValString(ByVal value As String)
value += " world!"
End Sub
Sub ByRefString(ByRef value As String)
value += " world!"
End Sub
Sub ByValStringBuilder(ByVal value As System.Text.StringBuilder)
value.Append(" world!")
End Sub
Sub ByRefStringBuilder(ByRef value As System.Text.StringBuilder)
value.Append(" world!")
End Sub
End Module
Think of C, and the difference between a scalar, like int, and an int pointer, and a pointer to an int pointer.
int a;
int* a1 = &a;
int** a2 = &a1;
Passing a is by value.
Passing a1 is a reference to a; it is the address of a.
Passing a2 is a reference to a reference; what is passed is the address of a1.
Passing a List variable using ByRef is analogous to the a2 scenario. It is already a reference. You are passing a reference to a reference. Doing that means that not only can you change the contents of the List, you can can change the parameter to point to an entirely different List. It also means you can not pass a literal null instead of an instance of List