1

Wicket では、 MetaDataKeyと呼ばれるものがあります。これらは、型付きのメタ情報を Wicket コンポーネントに保存するために使用されます。Wicket はシリアライゼーションを多用するため、Wicket の設計者は、単純なオブジェクト ID は信頼できないと判断し、MetaDataKey を抽象クラスにし、キーごとにサブクラスを作成し、キーがサブクラスのインスタンスであるかどうかを確認することを強制しました。 (Wicket ソースコードより):

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

したがって、キーを作成して何かを保存するには、次のようにします。

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

...

component.setMetaData(foo, "bar");

まず、シリアライゼーションでオブジェクト ID を使用するよりも、サブタイプを作成する方がうまく機能するのはなぜでしょうか?

次に、C# (匿名の内部クラスがない) で同様の機能を作成したい場合、どうすればよいでしょうか?

4

2 に答える 2

2

シリアライゼーションと同一性の問題は、シリアライゼーション プロセスが元のオブジェクトのクローンを実際に作成することです。ほとんどの時間、

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

に続く

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

それを強制することはできませんし、強制しませんx == y(そうあるべきですがx.equals(y)

本当にIDを使用したい場合は、ストリームからクラスのインスタンスを読み取ると、書き込まれたのと同じ(シングルトンの場合と同じ)インスタンスが実際に生成されるようにするカスタムシリアル化コードを作成する必要があります。これを正しく行うのは難しく、開発者に単純にマジック キーを宣言するように強制すると、API が非常に使いにくくなると思います。

最近では、enums を使用し、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; }
}

欠点は、共通の基本クラスから継承する列挙型を宣言できないことです (ただし、インターフェイスを実装することはできます)。そのため、MetaDataKey提供される可能性のあるサポート コード全体を何度も何度も手動でコーディングする必要があります。 . 上記の例では、これらすべてgetTypeが抽象基本クラスによって提供されているはずですが、 を使用したため提供できませんでしたenum

質問の2番目の部分については...残念ながら、私はそれに答えるのに十分なC#に習熟しているとは感じていません(すでにコメントに記載されているプレーンなプライベートクラスソリューションを使用する以外に)。

そうは言っても...(編集して、ベンの回答に対するコメントに表示された質問に回答します)同様のことを達成するための1つの可能な解決策(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));
 }

現在、物事は思い通りに機能していますが、主要なガイドを維持する負担があなたにかかっています。これは、Java ソリューションがこのかわいい匿名サブクラスのトリックで回避しようとしているものだと思います。

于 2011-01-25T01:42:07.160 に答える
1

サブタイプと参照アイデンティティの使用の理論的根拠に関しては、私はあなたと同じくらい暗闇の中にいます。推測する必要がある場合は、参照は基本的に派手なポインターであるため、それらの値はシリアライゼーション/デシリアライゼーションによって保持されるとは限りません。一意のオブジェクト ID をシミュレートする賢い方法は、Java コンパイラーが選択したものを使用することです。特定の匿名サブクラスを呼び出す。私は Java の仕様に詳しくないので、これが指定された動作なのか、さまざまなコンパイラの実装の詳細なのかはわかりません。私はここで完全に軌道から外れているかもしれません。

C# には匿名型がありますが、厳密に制限されています。特に、System.Object 以外から継承することはできず、インターフェイスを実装することもできません。それを考えると、この特定の手法を Java からどのように移植できるかわかりません。

シリアライゼーションによってオブジェクトの一意性を保持する 1 つの方法は、次のようになります。Wicket スタイルの基本クラスは、理論的には、オブジェクトの値を取得するaSystem.Guidまたは a のように、実行時に一意で構築時に生成されるプライベート メンバーを保持できます。System.IntPtr扱う。この値は、(逆) シリアル化して、参照の等価性の代用として使用できます。

[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 オブジェクトの実際の参照値を保存する方法は次のとおりです。これにより、必要なメモリが少し少なくなり、参照等価性の概念に少し忠実になります。

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
}
于 2011-01-25T01:42:59.727 に答える