weakref syntax to avoid circular reference

I have a socket class (mysocket) subclassed from SSLSocket with a property socktimer, in which I want to put a reference to my socketTimer class, which itself has a socket property in which I want to put a weak reference back to the socket. That way I avoid circular refs and all the relevant events (timerAction, socketDataAvailable, etc) will have access to the data they need to operate.

The expectation is that the socket gets used, then closed, and goes out of scope as the thread using it returns. Then I would expect the socket to get removed followed by the timer, as their refcounts go to zero.

The timer is instanced in the socket’s Constructor, with this as my first attempt:

Dim tim as socketTimer = new socketTimer me.socktimer = tim // In the socket, a reference to the timer tim.socket = SSLSocket (weakref (me)) // In the timer, a reference to the socket

Is this the right syntax for creating the weakref back to the socket? It compiles OK but I fear that all I’ve done is cast “me” twice.

Judging by some of the examples in the doc and also threads here, it looks more like I should change the socket property in the socketTimer class to be of type weakref, and then proceed thus:

Dim tim as socketTimer = new socketTimer me.socktimer = tim // In the socket, a reference to the timer tim.socket = new weakref (me) // In the timer, a reference to the socket

This also compiles and if correct then I assume that in the event handlers I would need to take socket.value and cast that to the appropriate socket class.

Is this a correct understanding?

Make the Timer’s Action event a method of your SSLSocket class, and assign it to the Timer using AddHandler and WeakAddressOf. This will let you reference both the Timer and the SSLSocket from within the Timer.Action event, and using WeakAddressOf prevents a circular reference:

[code]Class MySocket
Inherits SSLSocket

Private Property SockTimer As Timer

Sub Constructor()
Super.Constructor() ’ required when subclassing sockets
SockTimer = New Timer
’ this line assigns a weak reference of the handler method to the timer as its Action event handler
AddHandler SockTimer.Action, WeakAddressOf TimerActionHandler
End Sub

Private Sub TimerActionHandler(Sender As Timer)
’ within this handler, “Me” refers to the Socket and “Sender” refers to the Timer
End Sub
End Class[/code]

I agree with Andrew. If your Timer is tied to a specific class, you might as well use AddHandler and implement the code directly in the class. The only addition I’d make is to put the corresponding RemoveHandler into the Destructor.

But since your question is about WeakRef, this is how I handle it.

In the subordinate class, I create a private shadow property, say, mParent, and set it’s “Hidden” attribute. I then created a computed property, Parent, with this code:

Get (value As MyClass)
  if mParent is nil then
    return nil
  else
    return MyClass( mParent.Value )
  end if

Set (value As MyClass)
  if value is nil then
    mParent = nil
  else
    mParent = new WeakRef( value )
  end if

You now have a property that directly references the other object without creating a circular reference.

If you have many children that will reference the parent, create a property in the parent like MyWeakRef that stores and returns a single WeakRef to that object.

[quote=416522:@Andrew Lambert]Make the Timer’s Action event a method of your SSLSocket class, and assign it to the Timer using AddHandler and WeakAddressOf. This will let you reference both the Timer and the SSLSocket from within the Timer.Action event, and using WeakAddressOf prevents a circular reference:

[code]Class MySocket
Inherits SSLSocket

Private Property SockTimer As Timer

Sub Constructor()
Super.Constructor() ’ required when subclassing sockets
SockTimer = New Timer
’ this line assigns a weak reference of the handler method to the timer as its Action event handler
AddHandler SockTimer.Action, WeakAddressOf TimerActionHandler
End Sub

Private Sub TimerActionHandler(Sender As Timer)
’ within this handler, “Me” refers to the Socket and “Sender” refers to the Timer
End Sub
End Class[/code][/quote]
I am in fact already doing all of this. I omitted that from my OP as it didn’t seem pertinent to my question. And why does the use of WeakAddressOf (which I already do) help the circular reference issue? The circular reference issue, surely, is going to be caused by the the socket referencing the timer by having a property pointing at it, and likewise the timer having a property that refers back to the socket.

And @Kem Tekinay I don’t get the point at all, I’m afraid. I don’t have many children referencing the parent, just the one, and the linkages between them are set up in the socket’s constructor (that’s my intention, anyway). The socket then gets used until the thread using it terminates, which should mean the socket’s refcount goes to zero, which in turn causes the timer’s refcount to go to zero.

The weakref is set in the constructor and used in the timer’s action event, where the code is:

[code]Sub TimerEvent (t as timer)

Dim tim as socketTimer, socket as mySocket

tim = socketTimer (t)
socket = mySocket (tim.socket.value)

// code to resume thread follows

End Sub[/code]

Again, the compiler seems happy but I haven’t tried this yet.

If you set up AddHandler with “AddressOf” instead of “WeakAddressOf”, you’ve created a reference and that object will never go out of scope.

I’m not sure which point you mean as I made several. Forgetting the one about “multiple children”, I was describing a system to manage WeakRefs where you can access the object directly (and through the debugger) without having to manage the WeakRef yourself each time. It’s meant for convenience and readability more than function.

OK - I’ll study the AddressOf/WeakAddressOf issue a bit more.

Regarding the computed property and its get/set methods, I think in my case I would be using get once only and set once only, so in this instance that, to me, seems a bit heavy as each is a one-liner. But I can see the utility in cases where one might be doing set/get in many places in the code or what is needed to actually effect the get/set is many lines of code.

Thanks.