我有一个例程,它接受一个对象并对其进行一些处理。对象可能也可能不是可变的。
void CommandProcessor(ICommand command) {
// do a lot of things
}
同一命令实例有可能在处理器中循环。当发生这种情况时,事情就变得讨厌我想检测这些返回访问者并阻止他们被处理。问题是我怎样才能透明地做到这一点,即不打扰对象本身。
这是我试过的
Boolean Visited {get, set}
上添加了属性ICommand
。 我不喜欢这个,因为一个模块的逻辑显示在其他模块中。 ShutdownCommand
关注的是关闭,而不是簿记。此外,EatIceCreamCommand
可能始终返回False
,希望获得更多。一些非可变对象与setter有完全不同的问题。
我也不喜欢这个。 (1)表现。查找表变大了。我们需要进行线性搜索以匹配实例。 (2)不能依赖hashcode
。该对象可能不时伪造不同的哈希码。 (3)将对象保留在列表中可防止它们被垃圾收集。
我需要一种方法在实例(ICommand)上放置一些不可见的标记,只有我的代码可以看到。目前我不区分调用。只是祈祷相同的情况不要回来。有没有人有更好的想法来实现这个功能..?
答案 0 :(得分:3)
假设你无法阻止这种情况发生在逻辑上(尝试切断循环),我会选择你已经看过的HashSet
个命令。
即使对象违反了HashCode
和Equals
的合同(我将其视为问题),您也可以创建自己的IEqualityComparer<ICommand>
使用{{3}非虚拟地调用Object.GetHashCode
。 Equals
方法只会测试参考标识。因此,您的池将包含不同的实例,而不关心命令是否或如何覆盖Equals
和GetHashCode
。
这就留下了积累垃圾的问题。假设您没有定期清除池的选项,可以使用System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode
(或.NET 4的非泛型WeakReference
类)来避免保留对象。然后,您会经常发现所有“死”的弱引用,以防止累积那些。 (在这种情况下,您的比较器实际上是IEqualityComparer<WeakReference<T>>
,比较弱引用的目标以确定身份。)
它不是特别优雅,但我认为这是设计中固有的 - 你需要处理一个命令来改变状态,并且一个不可变对象不能按定义改变状态,所以你需要命令之外的状态。哈希集似乎是一种相当合理的方法,希望我已经明确表示如何避免你提到的所有三个问题。
编辑:我没有考虑的一件事是使用WeakReference<T>
使得删除条目变得很困难 - 当原始值被垃圾收集时,你将无法再找到它的哈希码。您可能需要使用仍然有效的条目创建新的HashSet
。或者使用您自己的LRU缓存,如评论中所述。