2

我正在为我正在使用的一系列系统编写一个不可知论的查看器。此查看器将向我展示我的数据的通用结构,而无需了解特定系统的上下文。

我正在尝试反序列化仅包含继承自的类型Foo<T>的内存流。从不可知论者的角度来看,我需要的所有数据都在 Foo 中。部分无关紧要。Foo<T>Foo<T>

类型 T 在另一个程序集中定义。在正常操作下,系统显然已经加载了所有适当的上下文程序集。问题是在运行查看器时,没有加载任何上下文程序集。当我尝试反序列化 Foo 的实例时,我显然得到了一个异常,因为没有加载引用的程序集。

我正在尝试检测是否已加载所有必需的引用程序集,从而知道是否尝试反序列化数据,或从类的其他方面重建我需要的数据。

我知道我可以使用一个非常简单的异常 try/catch 块来做到这一点,但是,这不是异常情况。我知道当我加载数据时这会发生数百次,如果不是数千次,这可能会给我带来一场噩梦,因为我喜欢打开异常中断。我还赞同说“异常 - 提示就在名称中”的思想流派,因此异常不应构成您的主要案例代码的一部分。

--------编辑 21/10/2013------------

请参阅此处以获取完整的说明性示例,但以下是重要的部分:

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();        
}

所以你把你喜欢的任何数据放在这里,你会得到一个完全限定的程序集名称列表,然后你可以单独存储。

假设Wibble是序列化 Foo 的类

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,版本=1.0.0.0,Culture=neutral,PublicKeyToken=nullgMyObject`1[[System.Int32,mscorlib,版本=4.0.0.0,Culture=neutral,PublicKeyToken=b77a5c561934e089]]n1n2str ]

或者,如果您可以控制序列化,请先使用文件中所需的信息(例如,仅 T 的类型和程序集)序列化对象,然后再使用 BinaryFormatter 中的数据。(如果您愿意,我可以对此进行扩展)。

要查看是否加载了任何对象,您可以使用Type.GetType("Assembly.Name.Space.ClassName")因为如果找不到类型,它会返回 null,但是对于“如何检查”的问题列出了其他方法如果命名空间、类或方法存在”,可以改为使用。

于 2013-10-22T10:06:32.320 回答