I have a routine which accepts an object and does some processing on it. The objects may or may-not be mutable.
void CommandProcessor(ICommand command) {
// do a lot of things
}
There is a probability that the same command instance loops back in the processor. Things turn nasty when that happens. I want to detect these return visitors and prevent them from being processed. question is how can I do that transparently i.e. without disturbing the object themselves.
here is what i tried
- Added a property
Boolean Visited {get, set}
on theICommand
.
I dont like this because the logic of one module shows up in other. The ShutdownCommand
is concerned with shutting down, not with the bookkeeping. Also an EatIceCreamCommand
may always return False
in a hope to get more. Some non-mutable objects have outright problems with a setter.
- privately maintain a lookup table of all processed instances. when an object comes first check against the list.
I dont like this either. (1) performance. the lookup table grows large. we need to do liner search to match instances. (2) cant rely on hashcode
. the object may forge a different hashcode from time to time. (3) keeping the objects in a list prevents them from being garbage collected.
I need a way to put some invisible marker on the instance (of ICommand) which only my code can see. currently i dont discriminate between the invocations. just pray the same instances dont come back. does anyone have a better idea to implement this functionality..?
Assuming you can't stop this from happening just logically (try to cut out the loop) I would go for a
HashSet
of commands that you've already seen.Even if the objects are violating the contracts of
HashCode
andEquals
(which I would view as a problem to start with) you can create your ownIEqualityComparer<ICommand>
which usesSystem.Runtime.CompilerServices.RuntimeHelpers.GetHashCode
to callObject.GetHashCode
non-virtually. TheEquals
method would just test for reference identity. So your pool would contain distinct instances without caring whether or how the commands overrideEquals
andGetHashCode
.That just leaves the problem of accumulating garbage. Assuming you don't have the option of purging the pool periodically, you could use
WeakReference<T>
(or the non-genericWeakReference
class for .NET 4) to avoid retaining objects. You would then find all "dead" weak references every so often to prevent even accumulating those. (Your comparer would actually be anIEqualityComparer<WeakReference<T>>
in this case, comparing the targets of the weak references for identity.)It's not particularly elegant, but I'd argue that's inherent in the design - you need processing a command to change state somewhere, and an immutable object can't change state by definition, so you need the state outside the command. A hash set seems a fairly reasonable approach for that, and hopefully I've made it clear how you can avoid all three of the problems you mentioned.
EDIT: One thing I hadn't considered is that using
WeakReference<T>
makes it hard to remove entries - when the original value is garbage collected, you're not going to be able to find its hash code any more. You may well need to just create a newHashSet
with the still-alive entries. Or use your own LRU cache, as mentioned in comments.