InvalidOperationException:参数对象"Scratch"的类型不是原始的 [英] InvalidOperationException: The type of the argument object 'Scratch' is not primitive

查看:63
本文介绍了InvalidOperationException:参数对象"Scratch"的类型不是原始的的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

所以有错误.

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:

  1. 程序集按需加载,因此您不能仅执行一次搜索,并且
  2. XmlSerializer一旦构造,就必须缓存在哈希表或字典中,然后重新使用以防止内存和资源泄漏.请参见此处了解详情.
  1. Assemblies are loaded on demand so you cannot perform the search just once, and
  2. 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆