您可以遍历应用程序域中的所有程序集,找到可从您的基本类型分配的所有类型,并在构造 XmlSerializer 时将它们作为已知类型传递。但是,一些警告:
- 程序集是按需加载的,因此您不能只执行一次搜索,并且
-
XmlSerializer 一旦构造完成,就必须缓存在哈希表或字典中并重复使用以防止内存和资源泄漏。详情请见here。
因此,您可以执行以下操作:
public static class XmlSerializerWithKnownTypeCreator<T>
{
static Dictionary<HashSet<Type>, XmlSerializer> table = new Dictionary<HashSet<Type>, XmlSerializer>(HashSet<Type>.CreateSetComparer());
public static XmlSerializer CreateKnownTypeSerializer<TRoot>()
{
return CreateKnownTypeSerializer(new Type [] {typeof(TRoot)});
}
public static XmlSerializer CreateKnownTypeSerializer(IEnumerable<Type> baseTypes)
{
var set = new HashSet<Type>(
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => baseTypes.Any(baseType => baseType.IsAssignableFrom(t))));
lock (table)
{
XmlSerializer serializer;
if (table.TryGetValue(set, out serializer))
return serializer;
table[set] = serializer = new XmlSerializer(typeof(T), set.ToArray());
return serializer;
}
}
}
然后这样称呼它:
var serializer = XmlSerializerWithKnownTypeCreator<DocumentRoot>.CreateKnownTypeSerializer<BaseMove>();
例如,
var serializer = XmlSerializerWithKnownTypeCreator<List<BaseClass>>.CreateKnownTypeSerializer<BaseMove>();
更新
如果序列化程序的参数化通用静态表看起来很奇怪(公平地说,确实如此),您可以将序列化程序存储在非通用全局哈希表中,如 documentation 中所建议的那样:
如果您使用任何其他构造函数,则会生成同一程序集的多个版本并且永远不会卸载,这会导致内存泄漏和性能下降。最简单的解决方案是使用前面提到的两个构造函数之一。否则,您必须将程序集缓存在 Hashtable 中,如下例所示。
但是,文档有点掩盖了如何为XmlSerializer 生成密钥。代码示例也不是线程安全的,可能是因为所有这些东西都可以追溯到 .Net 1.0。所以这里有一些逻辑可以在全局哈希表中正确地键入和回收具有已知额外类型的XmlSerializer:
public abstract class XmlserializerKey
{
readonly Type serializerType;
public XmlserializerKey(Type serializerType)
{
this.serializerType = serializerType;
}
protected Type SerializerType { get { return serializerType; } }
public override bool Equals(object obj)
{
if (ReferenceEquals(this, obj))
return true;
else if (ReferenceEquals(null, obj))
return false;
if (GetType() != obj.GetType())
return false;
XmlserializerKey other = (XmlserializerKey)obj;
if (other.serializerType != serializerType)
return false;
return true;
}
public override int GetHashCode()
{
int code = 0;
if (serializerType != null)
code ^= serializerType.GetHashCode();
return code;
}
public override string ToString()
{
return string.Format("Serializer type: " + serializerType.ToString());
}
}
public abstract class XmlserializerKeyWithExtraTypes : XmlserializerKey
{
static IEqualityComparer<HashSet<Type>> comparer;
readonly HashSet<Type> moreTypes = new HashSet<Type>();
static XmlserializerKeyWithExtraTypes()
{
comparer = HashSet<Type>.CreateSetComparer();
}
public XmlserializerKeyWithExtraTypes(Type serializerType, IEnumerable<Type> extraTypes)
: base(serializerType)
{
if (extraTypes != null)
foreach (var type in extraTypes)
moreTypes.Add(type);
}
protected Type[] MoreTypes { get { return moreTypes.ToArray(); } }
public override bool Equals(object obj)
{
if (!base.Equals(obj))
return false;
XmlserializerKeyWithExtraTypes other = (XmlserializerKeyWithExtraTypes)obj;
return comparer.Equals(moreTypes, other.moreTypes);
}
public override int GetHashCode()
{
int code = base.GetHashCode();
if (moreTypes != null)
code ^= comparer.GetHashCode(moreTypes);
return code;
}
}
public sealed class XmlSerializerKeyWithKnownTypes : XmlserializerKeyWithExtraTypes
{
public XmlSerializerKeyWithKnownTypes(Type serializerType, IEnumerable<Type> otherTypes)
: base(serializerType, otherTypes)
{
}
public XmlSerializer CreateSerializer()
{
return new XmlSerializer(SerializerType, MoreTypes);
}
}
public static class XmlSerializerHashTable
{
static Dictionary<object, XmlSerializer> dict;
static XmlSerializerHashTable()
{
dict = new Dictionary<object, XmlSerializer>();
}
public static XmlSerializer DemandSerializer(object key, Func<object, XmlSerializer> factory)
{
lock (dict)
{
XmlSerializer value;
if (!dict.TryGetValue(key, out value))
dict[key] = value = factory(key);
return value;
}
}
}
public static class XmlSerializerWithKnownDerivedTypesCreator
{
public static XmlSerializer CreateKnownTypeSerializer(Type type, IEnumerable<Type> extraTypes)
{
var allExtraTypes =
AppDomain.CurrentDomain.GetAssemblies()
.SelectMany(a => a.GetTypes())
.Where(t => extraTypes.Any(extraType => extraType.IsAssignableFrom(t)));
var key = new XmlSerializerKeyWithKnownTypes(type, allExtraTypes);
return XmlSerializerHashTable.DemandSerializer(key, k => ((XmlSerializerKeyWithKnownTypes)k).CreateSerializer());
}
}
然后以与调用等效的 XmlSerializer 构造函数相同的方式调用它:
public static void Test2()
{
List<BaseClass> list = new List<BaseClass>();
list.Add(new BaseClass());
list.Add(new MidClass());
list.Add(new DerivedClass1());
list.Add(new DerivedClass2());
var serializer = XmlSerializerWithKnownDerivedTypesCreator.CreateKnownTypeSerializer(list.GetType(), new Type[] { typeof(BaseClass) });
string xml = XmlSerializationHelper.GetXml(list, serializer, false);
Debug.WriteLine(xml);
// No assert below:
Debug.Assert(object.ReferenceEquals(serializer, XmlSerializerWithKnownDerivedTypesCreator.CreateKnownTypeSerializer(list.GetType(), new Type[] { typeof(BaseClass) })));
}