Click here to Skip to main content
15,881,139 members
Please Sign up or sign in to vote.
5.00/5 (1 vote)
See more:
There is a strange thing going on in my program. When I pass a parameter to a procedure ByVal, and change it within the procedure, the original (calling) parameter is changed too! Am I overlooking something simple, does it have to do with the structure of my program or have I stumbled on a bug in Visual Studio (2008)?

The structure of my (Windows Service) program is as follows:
VB
Private Sub JobTimer_Elapsed(ByVal sender As System.Object, ByVal e As System.Timers.ElapsedEventArgs) Handles JobTimer.Elapsed

    Dim lPortfolio As New Portfolio
    Dim lPortfolios As New Portfolios

    lPortfolios = lPortfolio.PerformHistoricalBacktest()

End Sub



Underneath you can find my Portfolio class. It contains mutual funds.
VB
Public Class Portfolio

    Private _funds As List(Of Fund)

    Public Sub New()
          _funds = New List(Of Fund)
    End Sub


    Public Overloads Function PerformHistoricalBacktest()

        Dim j As Integer
        Dim lNrOfPeriods As Integer = 8
        Dim lPortfolio As Portfolio
        Dim lPortfolios As New Portfolios


        For j = 1 To lNrOfPeriods

            Me.CalculateStatisticForEachFund()

            lPortfolio = New Portfolio
            lPortfolio = Me.GetFunds()  'Gets / returns part of the funds.
            lPortfolios.Add(portfolio:=lPortfolio)
            lPortfolio = Nothing

        Next j

        Return lPortfolios

    End Function


    Public Function GetFunds() As Portfolio

        Dim lPortfolioQualifying As Portfolio

        lPortfolioQualifying = New Portfolio

        For Each lFund As Fund In Me._funds

            With lFund

                If <checks if the particular lFund should be returned> Then

                    lPortfolioQualifying.AddFund(lFund)

                End If

            End With

        Next lFund

        Return lPortfolioQualifying

    End Function


    Public Sub AddFund(ByVal fundToAdd As Fund)

        'If I change 'fundToAdd' here, the calling parameter is changed too!!

        _funds.Add(item:=fundToAdd)

    End Sub

End Class



Underneath you can find my Portfolios class. It contains portfolios created at different moments in time.
VB
Public Class Portfolios


    Private _portfolios As List(Of Portfolio)


    Public Sub New()

        _portfolios = New List(Of Portfolio)

    End Sub


    Public Sub Add(ByVal portfolio As Portfolio)

        'If I change 'portfolio' here, the calling parameter is changed too!!

        _portfolios.Add(portfolio)

    End Sub

End Class
Posted

You are passing object references not value types.

Regards
Espen Harlinn
 
Share this answer
 
Comments
Manfred Rudolf Bihy 21-Jan-11 7:56am    
Correct! 5+
Espen Harlinn 21-Jan-11 12:22pm    
Thanks Manfred!
Nish Nishant 21-Jan-11 12:13pm    
5, proposed as answer.
Espen Harlinn 21-Jan-11 12:22pm    
Thanks Nishant!
Sergey Alexandrovich Kryukov 21-Jan-11 14:49pm    
As simple as that - my 5.
Answer 1 is correct

If you pass a reference to an object, it is the reference that cannot change when passed ByVal, not the contents.

If you assign or re-assign the object, the original reference will persist
once the call returns.
 
Share this answer
 
Comments
Espen Harlinn 21-Jan-11 7:52am    
5+ Nice elaboration
Manfred Rudolf Bihy 21-Jan-11 7:56am    
Good call! 5+
Gebbetje 21-Jan-11 8:56am    
Thank you both (Espen Harlinn & gazzasmith). But (a probably very basic question) how to solve it?
Do I have to change my 'Portfolio.AddFund' to copy the contents of 'fundToAdd' when adding it to '_funds' or is there a simpler solution?
Nish Nishant 21-Jan-11 12:13pm    
5, proposed as alternate answer.
Well, sounds like you need to make Portfolio or Fund immutable.
Make all the properties ReadOnly and pass in the values via a Constructor.
As an added convience, add a Clone() Function or Copy Constructor.

This will stop updates to individual properties and keep everything consistent.


VB
Public Class Fund
    Public ReadOnly Name As String
    Public ReadOnly Amount As Decimal

    Public Sub New(ByVal name As String, ByVal amount As Decimal)
        Me.Name = name
        Me.Amount = amount
    End Sub

    Public Sub New(ByVal copyOf As Fund)
        Me.Name = copyOf.Name
        Me.Amount = copyOf.Amount
    End Sub
End Class


This creates a nice thread safe class.

However, if you do need read/write properties, then make them so but still pass in a copy to AddFund. Say:

AddFund(New Fund(fund))

I am not sure why you just cannot use temp variables in the function either
instead of updating the object, but without the reat of the code its difficult to see whats going on.
 
Share this answer
 
Comments
Gebbetje 21-Jan-11 10:02am    
Thanks again.I'm afraid I need read/write properties.

"However, if you do need read/write properties, then make them so but still pass in a copy to AddFund. Say:
AddFund(New Fund(fund))"
I'll give it ago.
Gebbetje 21-Jan-11 11:39am    
FYI: When trying to implement the Clone function I run into the error message: "'Me' cannot be the target of an assignment.", when trying to use it (in the constructor of the Fund class).

Public Sub New(ByVal copyOf As Fund)
Me = Clone(copyOf)
End Sub
On the immutable class I specified earlier, you would need private members (not auto members) or private setters defined so that the properties can be assigned in the Copy Constructor or Clone method.

It has been difficult to help you with this as you have missed out the code that changes the Fund instance inside the method, and now you have missed out the Clone implementation, making it tricky to help you again.

I think your design may need a re-think.

Your code inside the Add() Method should not change the members of Fund or Portfolio as this creates a side effect. Add should only do that.

Maybe if you did whatever it is that changes the values, outside of Add(), on a copy of the data first.
 
Share this answer
 
Comments
Gebbetje 26-Jan-11 6:38am    
I'm not sure if you receive an e-mail when I post my reply to your post as an answer (number 6) as I did.
Your help is much appreciated!

"It has been difficult to help you with this as you have missed out the code that changes the Fund instance inside the method, and now you have missed out the Clone implementation, making it tricky to help you again."

In my program I'm not really changing the Fund instance in the Portfolio.AddFund method. This was just to make the question as clear and understandable as possible. (The "_funds.Add(item:=fundToAdd)" is of course the add method of a standard .NET List class.)

I implemented my clone function as follows:
Private Function Clone(ByVal fundToClone As Fund) As Fund
      Dim lClone As New Fund

      lClone.Name = fundToClone.Name
      lClone.Return = fundToClone.Return

      Return lClone
End Function


Any suggestions are very welcome.
 
Share this answer
 

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900