Results 1 to 15 of 15
  1. #1
    joelmeaders is offline Advanced Beginner
    Windows XP Access 2007
    Join Date
    Jan 2012
    Posts
    33

    Custom Classes/Objects - Recursion via ByRef & ByVal

    I have an issue I am struggling with and would like some insight if someone has it. My issue has to do with classes/objects not terminating due to recursive references (which aren't in this example). I've put together some test code which highly simplifies the issue but kind of shows a small portion of what I am running into.



    Class "clsDog"
    Code:
    Option Compare Database
    Option Explicit
    
    Implements clsIDog
    
    Private m_DogName As String
    
    Public Property Let clsIDog_DogName(val As String)
        m_DogName = val
    End Property
    Public Property Get clsIDog_DogName() As String
        clsIDog_DogName = m_DogName
    End Property

    Class "clsIDog"
    Code:
    Option Compare Database
    
    Property Let DogName(val As String): End Property
    Property Get DogName() As String: End Property

    Module "Module1"
    Code:
    Option Compare Database
    Option Explicit
    
    Public Sub TestDogs()
    
        Dim myDog1 As clsIDog
        Set myDog1 = New clsDog
        
        Dim myDog2 As clsIDog
        Set myDog2 = New clsDog
        
        Call ChangeDog1(myDog1)
        Call ChangeDog2(myDog2)
        
        Debug.Print "Dog Name 1: " & myDog1.DogName
        Debug.Print "Dog Name 2: " & myDog2.DogName
        
        Call CloseDogByVal(myDog1)
        Call CloseDogByRef(myDog2)
        
        If (myDog1 Is Nothing) Then Debug.Print "Dog1 ran away"
        If (myDog2 Is Nothing) Then Debug.Print "Dog2 ran away"
    
    End Sub
    
    Private Sub ChangeDog1(ByVal inpDog As clsDog)
        inpDog.clsIDog_DogName = "Fido"
    End Sub
    
    Private Sub ChangeDog2(ByRef inpDog As clsDog)
        inpDog.clsIDog_DogName = "Sasha"
    End Sub
    
    Private Sub CloseDogByVal(ByVal inpDog As clsDog)
        Set inpDog = Nothing
    End Sub
    
    Private Sub CloseDogByRef(ByRef inpDog As clsDog)
        Set inpDog = Nothing
    End Sub

    The output of running TestDog() is:
    Dog Name 1: Fido
    Dog Name 2: Sasha
    Dog2 ran away



    Why would the name of both dogs change using ByRef and ByVal but only one of the dogs remain after setting them to nothing? I understand the use of ByRef and ByVal but this makes absolutely no sense to me because it's sort of contradictory.

  2. #2
    joelmeaders is offline Advanced Beginner
    Windows 8 Access 2016
    Join Date
    Jan 2012
    Posts
    33
    For even more fun let's add a dog house into the mix.

    Class "clsDogHouse" (Use ByRef or ByVal, doesn't matter because you get the same result)
    Code:
    Option Compare Database
    Option Explicit
    
    
    Implements clsIDogHouse
    
    
    Private m_DogInHouse As clsIDog
    
    
    Private Property Set clsIDogHouse_DogInHouse(ByRef value As clsIDog)
        Set m_DogInHouse = value
    End Property
    Private Property Get clsIDogHouse_DogInHouse() As clsIDog
        Set clsIDogHouse_DogInHouse = m_DogInHouse
    End Property
    
    
    Public Sub clsIDogHouse_PutDogInHouse(ByRef inpDog As clsIDog)
        Set clsIDogHouse_DogInHouse = inpDog
    End Sub
    
    
    Public Function clsIDogHouse_IsOccupied() As Boolean
        clsIDogHouse_IsOccupied = False
        If Not (clsIDogHouse_DogInHouse Is Nothing) Then clsIDogHouse_IsOccupied = True
    End Function
    
    
    Public Sub clsIDogHouse_MagicAct()
        Set clsIDogHouse_DogInHouse = Nothing
    End Sub

    Class "clsIDogHouse"
    Code:
    Option Compare Database
    Option Explicit
    
    
    Property Set DogInHouse(ByRef value As clsIDog): End Property
    Property Get DogInHouse() As clsIDog: End Property
    
    
    Sub PutDogInHouse(ByRef inpDog As clsIDog): End Function
    
    
    Function IsOccupied() As Boolean: End Function
    
    
    Sub MagicAct(): End Sub
    Add this to Module1
    Code:
    Public Sub DogHouseTest()
    
    
        Dim myDog As clsIDog
        Set myDog = New clsDog
        
        Dim hisHouse As clsIDogHouse
        Set hisHouse = New clsDogHouse
        
        Call ChangeDog1(myDog)
        Call hisHouse.PutDogInHouse(myDog)
        
        Debug.Print "Your dog, " & myDog.DogName & _
                IIf(hisHouse.IsOccupied, ", is in the house", "is not in the house")
        
        Call hisHouse.MagicAct
        If (myDog Is Nothing) Then Debug.Print "Your dog performed a magic trick"
        
        If (myDog Is Nothing) Then Call CloseDogByVal(myDog)
        If (myDog Is Nothing) Then Call CloseDogByRef(myDog)
        If (myDog Is Nothing) Then Debug.Print "Your dog ran away..."
    
    
        Set hisHouse = Nothing
        If (hisHouse Is Nothing) Then Debug.Print "His house is gone now..."
        
        If (myDog Is Nothing) Then Call CloseDogByVal(myDog)
        If (myDog Is Nothing) Then Call CloseDogByRef(myDog)
        If (myDog Is Nothing) Then Debug.Print "Your dog ran away..."
    
    
    End Sub
    The output of running DogHouseTest() gives this:
    Your dog, Fido, is in the house
    His house is gone now...


    I am SO LOST! The dog is not gone but his house is, even though we set the dog to nothing in the dog house class and four times in DogHouseTest()

    It just doesn't make sense to me.

  3. #3
    orange's Avatar
    orange is offline Moderator
    Windows 10 Access 2010 32bit
    Join Date
    Sep 2009
    Location
    Ottawa, Ontario, Canada; West Palm Beach FL
    Posts
    16,722
    I don't do enough class modules to reply directly. However, I found this reference by Jeff Conrad/John Viescas
    that may be useful. Read a few pages....
    https://books.google.ca/books?id=hWN...0ByRef&f=false

  4. #4
    ItsMe's Avatar
    ItsMe is offline Sometimes Helpful
    Windows 8 Access 2013
    Join Date
    Aug 2013
    Posts
    7,862
    @Orange
    I clicked the link but I am not sure I saw the same thing you intended us to see. It seems I was viewing the search terms and not the search results.

    @joelmeaders
    The way I understand it is that you cannot pass an Object by Value and you can only "Call by Reference". I copied the code you provided in post #1. Even though it steps through all lines, I do not believe your CloseDogByVal function is working. So, I suppose using the watch window might help illustrate it.

  5. #5
    orange's Avatar
    orange is offline Moderator
    Windows 10 Access 2010 32bit
    Join Date
    Sep 2009
    Location
    Ottawa, Ontario, Canada; West Palm Beach FL
    Posts
    16,722

  6. #6
    ItsMe's Avatar
    ItsMe is offline Sometimes Helpful
    Windows 8 Access 2013
    Join Date
    Aug 2013
    Posts
    7,862
    Yeah, that is similar to what I was thinking is at the root of the symptom. When you start getting to larger sized variables, it needs to be Call by Reference.

  7. #7
    joelmeaders is offline Advanced Beginner
    Windows 8 Access 2016
    Join Date
    Jan 2012
    Posts
    33
    @ItsMe - You are correct that ByVal doesn't close the object. I've always passed objects using ByRef and just wanted to make a test using that example, Setting the dog name with ByVal does work even though closing it doesn't. The most confusing part is my second post where I cannot close the dog object from anywhere in any way once it's passed to the dog house class.

    The issue behind this involves a class that contains many other classes. Since I cannot close those other classes in order to close my main class I run out of database connections (2048 limit in Access).

  8. #8
    ItsMe's Avatar
    ItsMe is offline Sometimes Helpful
    Windows 8 Access 2013
    Join Date
    Aug 2013
    Posts
    7,862
    @joelmeaders
    I tried seeing if something obvious jumped out in the Watch window and it did not. It seemed to only reiterate your point of the "backward" behavior. I need to take care of a couple of things right now. But, I am intrigued and want to look at post # 2 when I get the chance. I suppose there may be something going on with the fact you are using an abstract class. I tried a couple things with post #1 example like switching the order of declarations. hmmmmm

  9. #9
    joelmeaders is offline Advanced Beginner
    Windows 8 Access 2016
    Join Date
    Jan 2012
    Posts
    33
    @orange - I have the 2003 version of that book! Unfortunately I don't believe it jumps into classes to the point I'm at.

  10. #10
    ItsMe's Avatar
    ItsMe is offline Sometimes Helpful
    Windows 8 Access 2013
    Join Date
    Aug 2013
    Posts
    7,862
    I would like to start by saying I am not an expert when it comes to class libraries, inheritance and all that jazz. I do find it interesting to look at puzzles like this, though.

    I looked at post #1 again and the symptom you are experiencing makes sense to me. The byVal is not working because you cannot supply an Object, Group of Elements, or Class as an argument to a parameter that way.

    When I look at your example in post #2, I am not fully understanding your public interface. As I follow the Private side I seem to feel I come upon a leaky abstraction when I get to the standard module. So although I follow the symptom you explained, I am not understanding why you are approaching things this way.

    Would it be helpful to initialize your Implementation Class using the Initialize Procedure and then Terminate your Implementation Class using the Terminate procedure?

    Code:
    Private Sub Class_Initialize()
    
    End Sub
    Private Sub Class_Terminate()
    
    End Sub
    I tried to find some members to place in there but they all seem to be on the public side.

  11. #11
    joelmeaders is offline Advanced Beginner
    Windows 8 Access 2016
    Join Date
    Jan 2012
    Posts
    33
    Quote Originally Posted by ItsMe View Post
    I would like to start by saying I am not an expert when it comes to class libraries, inheritance and all that jazz. I do find it interesting to look at puzzles like this, though.

    I looked at post #1 again and the symptom you are experiencing makes sense to me. The byVal is not working because you cannot supply an Object, Group of Elements, or Class as an argument to a parameter that way.

    When I look at your example in post #2, I am not fully understanding your public interface. As I follow the Private side I seem to feel I come upon a leaky abstraction when I get to the standard module. So although I follow the symptom you explained, I am not understanding why you are approaching things this way.

    Would it be helpful to initialize your Implementation Class using the Initialize Procedure and then Terminate your Implementation Class using the Terminate procedure?

    Code:
    Private Sub Class_Initialize()
    
    End Sub
    Private Sub Class_Terminate()
    
    End Sub
    I tried to find some members to place in there but they all seem to be on the public side.
    Thanks for taking a look. In my actual issue I do have Initialize and Terminate procedures along with a manual close procedure to clear all lower objects used in the classes. Initialize and Terminate run automatically - If you were to put some debug print lines in them they would fire off automatically when the class is initialized and terminated. I figured out classes weren't closing by putting some Debug.Print lines in my class terminate procedures and saw they weren't firing when I try to set them to nothing.

    The reason I take this route:
    I have some base classes I reuse everywhere through an mde reference called elDB. The big classes with interfaces based on recordsets are automatically generated based on information derived from the tables. The other base classes handle things such as various types of database connections and linking (SQL, mySQL, Access etc...), security, encryption, audit logging, email via relay servers and more. My most used base class is called RecordObj - It loads record(s) via table, query etc... It tracks historical changes, handles user rights such as read only, retrieves the data from the database and sends to the parent class, passes the parent class data to forms automatically, handles data conversion, supports transactions and rollbacks, offline data caching and use, and much more.

    Basically I build the tables and then hit a command and all classes with interfaces are automatically built. I even have my own events that fire off such as before/after load/save etc... I can then build all non-standard logic into the classes and the forms are just unbound data placeholders that don't do any real work aside from workflow. Forms (Workflow) -> ParentClasses (Operations/Processing) -> BaseClasses(CRUD/Security/Connections) -> Database)

  12. #12
    orange's Avatar
    orange is offline Moderator
    Windows 10 Access 2010 32bit
    Join Date
    Sep 2009
    Location
    Ottawa, Ontario, Canada; West Palm Beach FL
    Posts
    16,722
    I would be interested in seeing some of the classes you mention. Class modules are not well documented nor discussed with Access and vba.
    I'm sure others would be interested also. I think these would make an excellent addition to the Code repository, sample databases and/or Tutorials section. You seem to have more working knowledge of class modules with Access/vba than other users/participants.

  13. #13
    joelmeaders is offline Advanced Beginner
    Windows 8 Access 2016
    Join Date
    Jan 2012
    Posts
    33
    I would be happy to share what I've done. I'm almost to the point where it can be considered "portable" since it now gets configurations and paths from ini files and I've documented most everything. Once I get this bug figured out and clean a few things up I'll share a sample app/database and hopefully others can expand on the base.

  14. #14
    ItsMe's Avatar
    ItsMe is offline Sometimes Helpful
    Windows 8 Access 2013
    Join Date
    Aug 2013
    Posts
    7,862
    I had a couple minutes to check in with the forum. I want to look at this again when I have more time. I would like to comment that I understand the issue to be classes are not being destroyed.

    I can also start to guess why you are pushing for the Call By Value. Previous to this insight (byVal reasoning), I was curious about Private vs. Public. Now I am thinking this is an issue that deserves further exploration. All the while, I understand there can be circumstances where you simply need something to work and that is good enough reason to build out in a specific direction. Certainly, you have dependencies that are not average.

  15. #15
    ItsMe's Avatar
    ItsMe is offline Sometimes Helpful
    Windows 8 Access 2013
    Join Date
    Aug 2013
    Posts
    7,862
    I took another look at your doghouse thing. What I can see is that you are destroying instances that are in scope Private to the class. Then, you test an instance on the Public side to see if it is destroyed. I believe this is the example I am referring to ...
    Code:
        Call hisHouse.MagicAct
        If (myDog Is Nothing) Then Debug.Print "Your dog performed a magic trick"
    If you keep an eye on a couple objects from each side using the Watch and the Stack you can see them fall in and out of scope.

    Since I do not know all of the reasoning you have things set up the way you do. Perhaps I should ask, is it possible to manage more of this stuff within the Implementation Class, privately? Then, you can grab the private stuff as needed via a Friend Function.

    Maybe something like ...
    Friend Function NeedAccess() as clsIDog
    set NeedAccess = "private instance"
    end function

    Then destroy the new instance of NeedAccess that is in scope in the Public Side from the Public side. Without me personally building out a real world factory method I am not sure what else to suggest. In other words, I am not understanding why it is not possible to destroy the object wherever it may be.

Please reply to this thread with any new information or opinions.

Similar Threads

  1. Consecutive without recursion.
    By emihir0 in forum Queries
    Replies: 7
    Last Post: 10-28-2015, 06:22 PM
  2. Implementing Custom Class Objects-Where should they go?
    By Monterey_Manzer in forum Database Design
    Replies: 5
    Last Post: 04-07-2014, 12:15 PM
  3. VBA ByVal vs ByRef Passing Arguments
    By ylatodd in forum Programming
    Replies: 5
    Last Post: 10-22-2013, 02:49 PM
  4. Replies: 2
    Last Post: 03-13-2012, 03:01 PM
  5. Custom Objects as Records
    By FunWithNumbers in forum Access
    Replies: 0
    Last Post: 08-08-2011, 02:09 PM

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Other Forums: Microsoft Office Forums