在 Wicket 中,他们有一个叫做 MetaDataKey 的东西。 .这些用于在 Wicket 组件中存储类型化的元信息。由于 Wicket 大量使用序列化,Wicket 设计者认为简单的对象标识不可靠,因此将 MetaDataKey 设为抽象类,迫使您为每个键创建一个子类,然后检查该键是否是子类的实例(来自 Wicket 源代码):
public boolean equals(Object obj) {
return obj != null && getClass().isInstance(obj);
}
因此,要创建一个 key 并存储一些东西,我会做这样的事情:
private final static MetaDataKey<String> foo = new MetaDataKey<String>() {};
...
component.setMetaData(foo, "bar");
首先,为什么在序列化下使用子类型比使用对象身份更好?
其次,如果我想在 C# 中创建一个类似的工具(它缺少匿名内部类),我该怎么做?
最佳答案
序列化和身份的问题在于,序列化过程实际上创建了原始对象的克隆。大多数时候,
SomeThing x = ...;
ObjectOutputStream oos = ...;
oos.writeObject(x);
其次是
ObjectInputStream ois = ...;
SomeThing y = (SomeThing)ois.readObject()
不能也不会强制执行 x == y
(尽管应该是 x.equals(y)
)
如果您真的想使用身份,则必须编写自定义序列化代码,这会强制从流中读取您的类的实例实际上会产生与编写的实例相同的实例(与在单例中一样)。这很难做到正确,而且我认为,仅仅为了声明一个神奇的 key 而强制开发人员这样做会使 API 变得非常难以使用。
如今,人们可以使用 enum
,并依靠 VM 来强制执行单例字符。
enum MyMetaDataKey implements HyptheticalMetaDataKeyInterface {
TITLE(String.class),
WIDTH(Integer.class);
private final Class<?> type;
private MyMetaDataKey(Class<?> t) { type = t; }
public Class<?> getType() { return type; }
}
缺点是,您不能将枚举声明为从公共(public)基类继承(不过您可以让它实现接口(interface)),因此您必须手动编写整个支持代码,MetaDataKey
可能会一遍又一遍地为您提供。在上面的示例中,所有这些 getType
都应该由抽象基类提供,但它不能,因为我们使用了 enum
。
关于问题的第二部分...不幸的是,我觉得自己对 C# 的精通程度不足以回答这个问题(除了已经提到的使用评论中已经给出的普通私有(private)类解决方案之外)。
就是说...(编辑 以回答出现在对 Ben 的回答的评论中的问题)实现类似目标的一种可能解决方案(从某种意义上说,它与Java 解决方案):
[Serializable]
public class MetaDataKey<T> {
private Guid uniqueId;
private Type type;
public MetaDataKey(Guid key, Type type) {
this.uniqueId;
this.type = type;
}
public override boolean Equals(object other) {
return other is MetaDataKey && uniqueId == ((MetaDataKey)other).uniqueId;
}
}
可用于
class MyStuff {
private static MetaDataKey<String> key = new MetaDataKey<String>(new Guid(), typeof(String));
}
请忽略任何违反 C# 语言的行为。太久没用了。
这看起来像是一个有效的解决方案。然而,问题在于 key
常量的初始化。如果像上面的示例那样完成,每次启动应用程序时,都会创建一个新的 Guid 值并将其用作 MyStuff
的元数据值的标识符。因此,假设您有一些来自先前程序调用的序列化数据(例如,存储在文件中),它的键将具有与 MyStuff
的元数据键不同的 Guid 值.反序列化后有效的任何请求
String myData = magicDeserializedMetaDataMap.Get(MyStuff.key);
将失败——仅仅是因为 Guid 不同。因此,为了使示例工作,您必须具有持久的预定义 Guid:
class MyStuff {
private static Guid keyId = new Guid("{pre-define-xyz}");
private static MetaDataKey<String> key = new MetaDataKey<String>(keyId, typeof(String));
}
现在,一切都按预期进行,但是维护关键 Guid 的负担落在了您身上。我认为,这是 Java 解决方案试图通过这种可爱的匿名子类技巧来避免的。
关于c# - 匿名内部类在 Java 中作为键,但在 C# 中呢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4789090/