InvalidOperationException: 参数对象“Scratch"的类型不是原始类型 [英] InvalidOperationException: The type of the argument object 'Scratch' is not primitive
问题描述
所以有错误.
InvalidOperationException: 参数对象 'Scratch' 的类型不是原始类型
InvalidOperationException: The type of the argument object 'Scratch' is not primitive
我正在做的是序列化一个类列表(List
).BaseEnemy 类中还有一个类列表(List
).当我运行时,BaseEnemy 的列表会正确序列化.但是,BaseMoves 列表没有.Scratch
是一个派生自 BaseMove 的类,它存储在 List
中.
What I'm doing is serializing a list of classes (List<BaseEnemy>
). The BaseEnemy class also has a list in it of classes (List<BaseMoves>
). When I run things, the list of BaseEnemy serializes properly. However, the list of BaseMoves does not. Scratch
is a class that derives from BaseMove that is stored in the List<BaseMove>
.
那是我遇到的问题.我在这里找到了答案...HERE...
"使用传递派生类的 XmlInclude 属性标记 BaseMove 类作为参数:"
"Mark BaseMove class with XmlInclude attribute passing your derived class as parameter:"
[XmlInclude(typeof(Scratch))]
public class BaseMove
{
public BaseMove()
{
}
}
就像一个魅力!那我为什么要在这里发这个?新问题.我有数百 个源自 BaseMove 的移动.是否有快捷方式或者我必须在 XmlInclude 中编写每个typeof(...)"?
Works like a charm! So why am I posting this here? New question. I have HUNDREDS of moves that derive from BaseMove. Is there a shortcut or do I have to write every single "typeof(...)" in the XmlInclude?
编辑 - 我找到的另一个解决方案.
public static Type[] GetAllSubTypes(Type aBaseClass)
{
List<Type> result = new List<Type>();
Assembly[] assemblies = System.AppDomain.CurrentDomain.GetAssemblies();
foreach (Assembly a in assemblies)
{
Type[] types = a.GetTypes();
foreach (Type t in types)
{
if (t.IsSubclassOf(aBaseClass))
result.Add(t);
}
}
return result.ToArray();
}
然后在序列化期间我只是调用这个函数作为一个额外类型的数组.
Type[] extraTypes = GetAllSubTypes(typeof(BaseMove));
XmlSerializer xml = new XmlSerializer(typeof(List<BaseEnemy>), extraTypes);
推荐答案
您可以遍历应用程序域中的所有程序集,找到所有可从您的基类型分配的类型,并在构建您的 时将它们作为已知类型传递XmlSerializer
.但是,一些警告:
You could iterate through all the assemblies in your app domain, find all types assignable from your base type, and pass them as known types when constructing your XmlSerializer
. However, some caveats:
- 程序集是按需加载的,因此您不能只执行一次搜索,并且
XmlSerializer
,一旦构建,就必须缓存在哈希表或字典中并重复使用以防止内存和资源泄漏.请参阅此处 了解详情.
- Assemblies are loaded on demand so you cannot perform the search just once, and
- The
XmlSerializer
, once constructed, must be cached in a hash table or dictionary and reused to prevent memory and resource leaks. See here for details.
因此,您可以执行以下操作:
Thus, you could do something like:
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>();
更新
如果序列化器的参数化通用静态表看起来很奇怪(公平地说,确实如此),您可以将序列化器存储在非通用全局哈希表中,如文档:
If the parameterized generic static table of serializers looks weird (and to be fair, it kind of does), you can store the serializers in a non-generic global hash table, as is suggested in the documentation:
如果您使用任何其他构造函数,则会生成同一程序集的多个版本并且永远不会卸载,这会导致内存泄漏和性能不佳.最简单的解决方案是使用前面提到的两个构造函数之一.否则,您必须将程序集缓存在 Hashtable 中,如下例所示.
If you use any of the other constructors, multiple versions of the same assembly are generated and never unloaded, which results in a memory leak and poor performance. The easiest solution is to use one of the previously mentioned two constructors. Otherwise, you must cache the assemblies in a Hashtable, as shown in the following example.
然而,文档类型掩盖了如何为 XmlSerializer
生成密钥.代码示例也不是线程安全的,可能是因为所有这些内容都可以追溯到 .Net 1.0.因此,这里有一些逻辑可以在全局哈希表中正确键入和回收具有已知额外类型的 XmlSerializer
:
However, the doc sort of glosses over how to generate a key for the XmlSerializer
. The code sample also isn't thread-safe, perhaps since all this stuff dates back to .Net 1.0. So here's some logic that will correctly key and recycle an XmlSerializer
with known extra types, in a global hash table:
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
构造函数:
And then call it in the same way as you would call the equivalent XmlSerializer
constructor:
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) })));
}
这篇关于InvalidOperationException: 参数对象“Scratch"的类型不是原始类型的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!