The following code does not work as I’d expect it to - it appears that when using Delegates using AddressOf, the equality operator works as expected (foo=bar), but when using WeakAddressOf, foo <> bar.
Bug?
Sub Action()
dim foo as CallbackMethod
dim bar as CallbackMethod
foo = AddressOf FooBar
bar = AddressOf FooBar
if foo=bar then
MsgBox "foo = bar when using AddressOf" // THIS IS TRUE
else
MsgBox "foo <>= bar when using AddressOf"
end if
foo = WeakAddressOf FooBar
bar = WeakAddressOf FooBar
if foo=bar then
MsgBox "foo = bar when using WeakAddressOf"
else
MsgBox "foo <> bar when using WeakAddressOf" // THIS IS TRUE
end if
End Sub
So how this affects me is that I’ve written a generic “Timer callback” object - you can pass it a delegate, and it sets up a timer and calls the method when the timer fires. Works great with strong delegates, and has a method that will cancel all pending timers for a given method, which also works.
When I started to try using Weak delegates, this mechanism failed: I’m no longer able to Cancel an existing timer, because I can’t tell if a the weak delegate that’s being requested to be cancelled matches the weak delegate on file. So, the timer is not canceled and it ends up firing.
Now, since I’m using a Weak delegate, you’d think that would be OK - however, trying to .Invoke() a weak delegate gives a NilObjectException.
I can probably work around this by using strong delegates and canceling them in the window’s CancelClose event rather than the Close event, but this is only a band-aid patch…
A workaround for this (not fully tested) is to assign the delegates to Ptrs and compare those
foo = WeakAddressOf FooBar
bar = WeakAddressOf FooBar
dim p1 As Ptr = foo
dim p2 As Ptr = bar
if p1 = p2 then Msgbox "=" else Msgbox "<>" //messages =
You can’t cast delegates to Ptrs inline, must implicitly assign them as above. So maybe make a method to clean it up where the casting happens across the parameter line
Function weakDelegatesEqual(d1 As Ptr, d2 As Ptr) As Boolean
return d1 = d2
End Function
[quote=15573:@doofus]For this you could also store a WeakRef to the object. When .Value goes nil assume the associated weak delegate is invalid.
Or enclose in a try/catch, ugs :P[/quote]
Good tips, I may try those.
I’ve also stumbled upon another oddity with delegates, which I’ll report in a new feedback case if I can isolate it. It appears that some combination of:
being in a class destructor
having a delegate on the call stack
having a delegate which points to the object being destroyed
being in a Window.Close event
When all happening together, trigger a weird infinite loop where the object’s destructor is called over and over. Naturally it only happens in a complex application, not in a simple test case. Anyone have any ideas about this?
[quote=15572:@doofus]A workaround for this (not fully tested) is to assign the delegates to Ptrs and compare those
foo = WeakAddressOf FooBar
bar = WeakAddressOf FooBar
dim p1 As Ptr = foo
dim p2 As Ptr = bar
if p1 = p2 then Msgbox "=" else Msgbox "<>" //messages =
You can’t cast delegates to Ptrs inline, must implicitly assign them as above. So maybe make a method to clean it up where the casting happens across the parameter line
Function weakDelegatesEqual(d1 As Ptr, d2 As Ptr) As Boolean
return d1 = d2
End Function[/quote]
I’d recommend against casting a delegate that came from an instance method to a pointer. There’s nothing useful you can do with that pointer (other than compare it with another pointer, apparently), so it might raise an exception in the future. The rationale behind the exception is that I’ve seen a lot of people try to pass instance methods to declares and crashes ensuing.