简体   繁体   English

在Java中,匿名内部类作为键,但是在C#中呢?

[英]Anonymous inner classes as keys in Java, but what in C#?

In Wicket, they have something called a MetaDataKey . 在Wicket中,它们有一个称为MetaDataKey的东西。 These are used to store typed meta information in Wicket components. 这些用于将类型化的元信息存储在Wicket组件中。 Since Wicket makes heavy use of serialization, the Wicket designers decided that simple object identity would not be reliable and so made MetaDataKey an abstract class, forcing you to create a subclass for each key and then check to see if the key is an instance of subclass (from the Wicket source code): 由于Wicket大量使用序列化,因此Wicket设计人员认为简单的对象标识将不可靠,因此将MetaDataKey设置为抽象类,从而迫使您为每个键创建一个子类,然后检查该键是否为子类的实例。 (来自Wicket源代码):

public boolean equals(Object obj) {
    return obj != null && getClass().isInstance(obj);
}

Thus, to create a key and store something I would do something like this: 因此,要创建密钥并存储内容,我将执行以下操作:

private final static MetaDataKey<String> foo = new MetaDataKey<String>() {};

...

component.setMetaData(foo, "bar");

First, why would making a subtype work better than using object identity under serialization? 首先,为什么要使子类型比在序列化下使用对象标识更好?

Second, if I wanted to create a similar facility in C# (which lacks anonymous inner classes), how would I go about it? 其次,如果我想在C#中创建类似的功能(缺少匿名内部类),我将如何处理?

The problem with serialization and identity is, that the serialization process actually creates a clone of the original object. 序列化和标识的问题是,序列化过程实际上会创建原始对象的克隆。 Most of the time, 大多数时候,

SomeThing x = ...;
ObjectOutputStream oos = ...;
oos.writeObject(x);

followed by 其次是

ObjectInputStream ois = ...;
SomeThing y = (SomeThing)ois.readObject()

cannot and does not enforce that x == y (though it should be the case, that x.equals(y) ) 不能也不会强制x == y (尽管应该是x.equals(y)

If you really wanted to go with identity, you'd have to write custom serialization code, which enforces, that reading an instance of your class from the stream yields actually the same (as in singleton) instance that was written. 如果您真的想使用身份,则必须编写自定义的序列化代码,该代码必须执行,即从流中读取类的实例所产生的结果实际上与编写的实例相同(与在单例中一样)。 This is hard to get right, and I think, forcing developers to do that simply to declare a magic key would make the API quite hard to use. 很难做到这一点,我认为,迫使开发人员仅声明一个魔术密钥就将使该API难以使用。

Nowadays, one could use enum s, and rely on the VM to enforce the singleton character. 如今,人们可以使用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; }
}

The disadvantage is, that you cannot declare you enum to inherit from the common base class (you can have it implement interfaces, though), so you would have to manually code the entire support code, which MetaDataKey might provide for you, over and over again. 缺点是,您不能声明要枚举从通用基类继承(但是您可以使它实现接口),因此您将不得不手动编写整个支持代码,而MetaDataKey可能会一遍又一遍地为您提供支持再次。 In the example above, all this getType should have been provided by an abstract base class, but it couldn't because we used enum . 在上面的示例中,所有这些getType应该已经由抽象基类提供,但是由于我们使用了enum ,所以不能提供。

As to the second part of the question... Unfortunately, I don't feel proficient enough in C# to answer that (besides the already mentioned use a plain private class solution already given in the comments). 至于问题的第二部分...不幸的是,我对C#的理解不够熟练(除了已经提到的使用注释中已经给出的普通私有类解决方案)。

That said... ( Edit to answer the questions which appeared in the comments on Ben's answer) One possible solution to achieve something similar (in the sense, that it would be just as usable as the Java solution): 就是说...( 编辑以回答对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;
    }
}

which may be used as in 可用于

class MyStuff {
    private static MetaDataKey<String> key = new MetaDataKey<String>(new Guid(), typeof(String));
}

Please ignore any violations of the C#-language. 请忽略任何违反C#语言的行为。 It's too long since I used it. 自从我使用它已经太久了。

This may look like a valid solution. 这看起来像是一个有效的解决方案。 The problem, however, lies in initialization of the key constant. 但是,问题在于key常数的初始化。 If it is done like in the example above, each time, the application is started, a new Guid value is created and used as the identifier for MyStuff 's meta-data value. 如果像上面的示例中那样完成操作,则每次启动应用程序时,都会创建一个新的Guid值并将其用作MyStuff的元数据值的标识符。 So if, say, you have some serialized data from a previous invocation of the program (say, stored in a file), it will have keys with a different Guid value of MyStuff 's meta-data key. 因此,例如,如果您从程序的先前调用中获得了一些序列化数据(例如,存储在文件中),则它将具有键,其键值与MyStuff的元数据键的Guid值不同。 An effectively, after deserialization, any request 反序列化后的任何请求都有效

 String myData = magicDeserializedMetaDataMap.Get(MyStuff.key);

will fail -- simply because the Guids differ. 会失败-仅仅是因为准则不同。 So, in order to make the example work, you have to have persistent pre-defined Guids: 因此,为了使示例正常工作,您必须具有持久的预定义Guid:

 class MyStuff {
     private static Guid keyId = new Guid("{pre-define-xyz}");
     private static MetaDataKey<String> key = new MetaDataKey<String>(keyId, typeof(String));
 }

Now, things work as desired, but the burden of maintaining the key Guids has come upon you. 现在,一切都按预期进行,但是维护关键Guid的负担已经由您承担。 This is, I think, what the Java solution tries to avoid with this cute anonymous subclass trick. 我认为,这就是Java解决方案通过这种可爱的匿名子类技巧可以避免的事情。

As far as the rationale for subtyping vs using reference identity, I'm as much in the dark as you. 至于子类型化与使用引用身份的基本原理,我和您一样无所适从。 If I had to guess, I'd say that as references are basically fancy pointers, their value isn't necessarily guaranteed to be retained through serialization/deserialization, and a clever way to simulate a unique object ID is with whatever the Java compiler chooses to call a particular anonymous subclass. 如果我不得不猜测,我会说由于引用基本上是奇特的指针,因此不一定要保证通过串行化/反序列化来保留它们的值,并且模拟唯一对象ID的聪明方法是使用Java编译器选择的任何方法调用特定的匿名子类。 I'm not too conversant with the Java spec, so I don't know if this is specified behavior or an implementation detail of the various compilers. 我不太熟悉Java规范,所以我不知道这是指定的行为还是各种编译器的实现细节。 I may be entirely off track here. 我可能在这里完全偏离了轨道。

C# has anonymous types, but they are severely limited - in particular, they cannot inherit from anything other than System.Object and can implement no interfaces. C#具有匿名类型,但它们受到严格限制-特别是,它们不能继承自System.Object以外的任何内容,并且不能实现任何接口。 Given that, I don't see how this particular technique can be ported from Java. 鉴于此,我看不到如何从Java移植此特定技术。

One way to go about retaining object uniqueness through serialization could go thusly: The Wicket-style base class could, in theory, retain a private member that is runtime-unique and generated on construction, like a System.Guid or a System.IntPtr that gets the value of the object's handle. 因此,可以通过序列化保持对象唯一性的一种方法是:Wicket风格的基类在理论上可以保留运行时唯一的,并在构造上生成的私有成员,例如System.GuidSystem.IntPtr 。获取对象的句柄的值。 This value could be (de)serialized and used as a stand-in for reference equality. 此值可以(反)序列化,并用作参考相等性的替代。

[Serializable]
public class MetaDataKey<T>
{
    private Guid id;

    public MetaDataKey(...)
    {
        this.id = Guid.NewGuid();
        ....
    }

    public override bool Equals(object obj)
    {
        var that = obj as MetaDataKey<T>;
        return that != null && this.id == that.id;
    }
}

EDIT Here's how to do it by saving the object's actual reference value; 编辑这是通过保存对象的实际参考值来实现的。 this takes a bit less memory, and is slightly more true to the notion of reference-equality. 这会占用较少的内存,并且对于引用相等的概念稍微更真实一些。

using System.Runtime.InteropServices;

[Serializable]
public class AltDataKey<T>
{
    private long id;  // IntPtr is either 32- or 64-bit, so to be safe store as a long.

    public AltDataKey(...)
    {
        var handle = GCHandle.Alloc(this);
        var ptr = GCHandle.ToIntPtr(handle);

        id = (long)ptr;

        handle.Free();
    }

    // as above
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM