2

私が使用しているシステムのコレクション用に、不可知論者のビューアーを作成しています。このビューアは、特定のシステムのコンテキストを知らなくても、データの一般的な構造を表示します。

から継承する型のみFoo<T>を含むメモリ ストリームを逆シリアル化しようとしています。不可知論者の視点から見ると、私が必要とするすべてのデータは Foo にあります。部分は関係ありません。Foo<T>Foo<T>

型 T は別のアセンブリで定義されています。通常の操作では、システムには適切なコンテキスト アセンブリがすべて読み込まれていることは明らかです。問題は、ビューアの実行時に、コンテキスト アセンブリがまったく読み込まれないことです。Foo のインスタンスを逆シリアル化しようとすると、参照されているアセンブリが読み込まれていないため、明らかに例外が発生します。

必要なすべての参照アセンブリが読み込まれているかどうかを検出しようとしているため、データを逆シリアル化するか、クラスの他の側面から必要なデータを再構築するかを判断します。

非常に単純な例外 try/catch ブロックを使用してこれを実行できることはわかっていますが、これは例外ケースではありません。これは、データをロードするときに何千回とは言わないまでも、何百回も発生することを知っています。ブレーク オン 例外をオンにしたいので、これは悪夢の原因になる可能性があります。私はまた、「例外 - ヒントは名前にある」という考え方に同意しているため、例外は主要なケース コードの一部を構成するべきではありません。

--------2013 年 10 月 21 日編集 ------------

完全な説明用の例については、こちらを参照してください。重要な部分は次のとおりです。

共通に定義された Foo クラス:

[Serializable]
public class Foo
{
    public string Agnostic { get; set; }
}

[Serializable]
public class Foo<T> : Foo
{
    public string Contextual { get; set; }
}

コンテキスト保存:

BinaryFormatter bf = new BinaryFormatter();
FileInfo tempFile = TempFileGetter.GetTempFile();


Foo<Bar> fooBar = new Foo<Bar>();
fooBar.Agnostic = "Agnostic";
fooBar.Contextual = "Contextual";


using (var fs = tempFile.OpenWrite())
{
   bf.Serialize(fs, fooBar);
   fs.Flush();
}

不可知ローディング:

BinaryFormatter bf = new BinaryFormatter();
FileInfo tempFile = TempFileGetter.GetTempFile();

using (var fs = tempFile.OpenRead())
{
   Foo foo = (Foo)bf.Deserialize(fs);
   Controls.DataContext = foo;
}

つまり、このコードにはロケット科学は何もありません。「不可知論者」ビューアーがコンテキスト ビューアーを参照としてロードする場合、正常にロードされますが、常にそうするとは限らないため、これを行いたくありません。ロードするコンテキスト ライブラリがあります。

4

2 に答える 2

0

私がしたことは、内容を分析してどの参照が必要で、個別にシリアル化されるかを確認するシリアル化コンテナーを作成することでした。

[serializable]
public class Container
{
    private IEnumerable<object> data;
    public Container(IEnumerable data);

    public string[] GetFullyQualifiedReferences();        
}

したがって、好きなデータをここに入力すると、完全修飾アセンブリ名のリストが得られ、それを個別に保存できます。

WibbleFooをシリアライズしているクラスであると仮定します

public class Wibble : ISerializable
{        

    public string Agnostic { get { return agnostic; } }

    public void GetObjectData(SerializationInfo info, StreamingContext context)
    {
        //construct a container based on my data.
        Container container = new Container(new object[]{myFoo});
        info.AddValue("RequiredReferences",container.GetFullyQualifiedReferences());

        byte[] containerData = Serialize(container);
        info.AddValue("TypeUnsafeData",containerData);

        //store some safe typed versions of the context data, if necessary.
        info.AddValue("SafeValues", GetSafeValues())
    }

    public Wibble(SerializationInfo info, StreamingContext context)
    { 
        var requiredAssemblies = 
           (string[])info.GetValue("RequiredReferences",typeof(string[]));

        if(AreAssembliesLoaded(requiredAssemblies )))
        {
           //deserialise the container as normal
        }
        else
        {
           //instead, load the "safe" data that we previously stored.
        }

    }

}

図を考えるとこれが完全に意味をなさないことはわかっていますが、図は実装の問題の抽象化がわずかに不完全ですが、ソリューションは両方で機能するはずです (私の実装で機能することはわかっています!)

次の手順に進み、任意のコンテナーに必要な特定のアセンブリを一覧表示するコンテナーをコンテナー内に配置することもできますが、この図では実際には必要ありません。

- - - アップデート - - - -

この実装には、現状ではわずかな問題があります。つまり、コンテキスト アセンブリのバージョン番号を更新すると、デシリアライザーが古いアセンブリを見つけられなくなります。したがって、次のいずれかを行う必要があります。

非シリアル化コードがアセンブリの最新バージョンを見つけてクエリできるかどうかを確認できるようにするための何らかのメカニズムを提供します。

- -また - -

バージョン管理の影響を受けないように、完全修飾名の等価性を更新します。

于 2013-10-22T13:58:54.247 に答える
-1

BinaryFormatter にはこの情報が含まれています。シリアライズされたデータを MemoryStream に読み込んで文字列に変換してみてください。

// There's probably a better way to do this:
new String(memoryStream.GetBuffer().Select<byte, char>(b => (char)b).ToArray());

あなたはAssembly.Name.MyObject<int>解析するのが苦痛のように見えるものになるでしょうが、それは実行できるはずです:

[ÿÿÿÿBAssembly.Name, Version=1.0.0.0, Culture=neutral, PublicKeyToken=nullgMyObject`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]n1n2str ]

または、シリアル化を制御できる場合は、最初にファイルで必要な情報 (たとえば、T の型とアセンブリのみ) を使用してオブジェクトをシリアル化し、次に BinaryFormatter からのデータをシリアル化します。(必要に応じてこれを拡張できます)。

オブジェクトがロードされているかどうかを確認するには、タイプが見つからない場合は null を返すType.GetType("Assembly.Name.Space.ClassName")を使用できますが、 「確認方法」という質問には他のメソッドがリストされています名前空間、クラス、またはメソッドが存在する場合」を代わりに使用できます。

于 2013-10-22T10:06:32.320 に答える