我正在为我正在使用的系统集合编写一个不可知的查看器。此查看器将向我显示我的数据的通用结构,而无需了解特定系统的上下文。
我正在尝试对仅包含Foo<T>
类型Foo<T>
的内存流进行反序列化,其中Foo
继承自<T>
。从不可知论的观点来看,我需要的所有数据都在Foo中。 [Serializable]
public class Foo
{
public string Agnostic { get; set; }
}
[Serializable]
public class Foo<T> : Foo
{
public string Contextual { get; set; }
}
部分无关紧要。
类型T在另一个程序集中定义。在正常操作下,系统显然已加载所有适当的上下文程序集。问题是在运行查看器时,没有加载任何上下文程序集。当我试图反序列化Foo的实例时,我显然得到一个异常,因为没有加载引用的程序集。
我正在尝试检测是否已加载所有必需的引用程序集,因此知道是否尝试反序列化数据,或者从类的其他方面重构我需要的数据。
我知道我可以使用一个非常简单的异常try / catch块来做到这一点,但是,这不是一个例外情况。我知道当我加载数据时,这将发生数百次,甚至数千次,这可能会让我成为一场噩梦,因为我希望在打开异常时休息一下。我还订阅了一个思想学校,上面写着“异常 - 提示在名称中”,因此异常不应构成主要案例代码的一部分。
-------- edit 21/10/2013 ------------
请参阅here以获取完整的说明性示例,但以下是重要的内容:
Foo类,共同定义:
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;
}
不可知载荷:
{{1}}
我的意思是,在这段代码中没有什么火箭科学,并且,如果“不可知”的查看器加载上下文查看器作为参考,那么它加载正常,但是,我不想这样做,因为我们赢了总是要加载上下文库。
答案 0 :(得分: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.
}
}
}
我知道这个插图并没有完全合理,但插图是我实现问题的略微不完美的抽象,但解决方案应该适用于两者(我知道它适用于我的实现!)
您可以进行下一步,在容器中放置一个容器,列出任何容器的特定必需程序集,但在此图中并不是必需的。
------ -------更新
此实现存在一个小问题,即如果更新上下文程序集版本号,则反序列化程序将找不到旧程序集。所以,你要么:
提供某种机制,允许deseralize代码查看它是否可以找到程序集的现代版本然后查询它,
--- ---或
更新完全限定名称的相等性,使其对版本控制不敏感。
答案 1 :(得分:-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中的数据。 (如果你愿意,我可以扩展这个。)
要查看是否加载了任何对象,可以使用Type.GetType("Assembly.Name.Space.ClassName"),因为如果找不到类型,则返回null,但是可以使用其他方法列出问题"how to check if namespace, class, or method exists"