InvalidOperationException:参数对象“Scratch”的类型不是原始的

本文关键字:类型 原始 Scratch 参数 对象 InvalidOperationException | 更新日期: 2023-09-27 18:33:42

所以有错误。

无效操作异常:参数对象"Scratch"的类型不是原始的

我正在做的是序列化类列表(List<BaseEnemy>(。BaseEnemy 类中还有一个类列表 ( List<BaseMoves> (。当我运行东西时,BaseEnemy 的列表会正确序列化。但是,BaseMoves列表没有。 Scratch 是一个派生自存储在 List<BaseMove> 中的 BaseMove 的类。

这就是 我遇到的问题。我在这里找到了答案...这里。。。

"使用 XmlInclude 属性标记 BaseMove 类,通过派生类 作为参数:">

[XmlInclude(typeof(Scratch))]
public class BaseMove
{
    public BaseMove()
    {
    }
}

像魅力一样工作!那么我为什么要在这里发布这个呢?新问题。我有数百个来自BaseMove的动作。是否有快捷方式,或者我是否必须在 XmlInclude 中编写每个"typeof(...("?

编辑 - 我找到的另一个解决方案。

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);

InvalidOperationException:参数对象“Scratch”的类型不是原始的

您可以循环访问应用程序域中的所有程序集,查找可从基类型分配的所有类型,并在构造XmlSerializer时将它们作为已知类型传递。 但是,需要注意一些:

  1. 程序集是按需加载的,因此您不能只执行一次搜索,并且
  2. XmlSerializer一旦构造,必须缓存在哈希表或字典中并重复使用以防止内存和资源泄漏。 详情请看这里。

因此,您可以执行以下操作:

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>();

更新

如果序列化程序

的参数化泛型静态表看起来很奇怪(公平地说,它确实如此(,则可以将序列化程序存储在非泛型全局哈希表中,如文档中所示:

如果使用任何其他构造函数,则会生成同一程序集的多个版本,并且永远不会卸载,这会导致内存泄漏和性能不佳。最简单的解决方案是使用前面提到的两个构造函数之一。否则,必须在哈希表中缓存程序集,如以下示例所示。

但是,该文档对如何为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) })));
    }