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>
). BaseEnemy类还具有一个类列表(List<BaseMoves>
).当我运行事物时,BaseEnemy的列表会正确序列化.但是,BaseMoves列表没有. Scratch
是从BaseMove派生的类,该类存储在List<BaseMove>
中.
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>
.
那我遇到的问题 WAS .我在这里找到了答案... HERE ...
That WAS the problem that I was having. I found the answer on here...HERE...
使用传递给您的派生类的XmlInclude属性标记BaseMove类 作为参数:"
"Mark BaseMove class with XmlInclude attribute passing your derived class as parameter:"
[XmlInclude(typeof(Scratch))]
public class BaseMove
{
public BaseMove()
{
}
}
像魅力一样工作!那我为什么要在这里发布呢?新问题.我有 HUNDREDS 条来自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();
}
然后在序列化过程中,我只调用此函数以用作extraTypes数组.
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:
如果使用任何其他构造函数,则将生成同一程序集的多个版本,并且永远不会将其卸载,这会导致内存泄漏和性能下降.最简单的解决方案是使用前面提到的两个构造函数之一.否则,必须将程序集缓存在哈希表中,如下面的示例所示.
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屋!