You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#序列化含未知派生类型的ObservableCollection<ModelRun>问题

问题:序列化含插件动态引入派生类型的ObservableCollection失败

在序列化包含插件引入的未知派生类型(如ScenarioModelRun)的ObservableCollection<ModelRun>时,调用XmlSerializer.Serialize报错:

The type ScenarioModelRun may not be used in this context. To use ScenarioModelRun as a parameter, return type, or member of a class or struct, the parameter, return type, or member must be declared as type ScenarioModelRun (it cannot be object). Objects of type ScenarioModelRun may not be used in un-typed collections, such as ArrayLists.

以下是针对该场景的可行解决方案:

方法1:运行时收集派生类型,创建带已知类型的XmlSerializer

由于派生类型由插件动态引入,设计时无法预知,需在运行时收集所有ModelRun的派生类型,再传入XmlSerializer构造函数:

// 遍历当前应用域所有程序集,收集ModelRun的非抽象派生类型
var derivedTypes = AppDomain.CurrentDomain.GetAssemblies()
    .SelectMany(asm => asm.GetTypes())
    .Where(t => t.IsSubclassOf(typeof(ModelRun)) && !t.IsAbstract)
    .ToArray();

// 创建包含已知派生类型的XmlSerializer
XmlSerializer serializer = new XmlSerializer(typeof(ObservableCollection<ModelRun>), derivedTypes);
serializer.Serialize(writer, property.GetValue(this));

注意:需确保插件程序集已被加载到当前应用域后再执行类型收集逻辑。

方法2:手动遍历集合元素,按实际类型序列化(推荐)

放弃直接序列化整个集合,改为逐个处理元素,记录每个元素的实际类型,确保派生类型能被正确序列化:

修改WriteXml中的对应逻辑:

if (property.Name == "ModelRuns" || property.Name == "SavedModelRnuns")
{
    writer.WriteStartElement(property.Name);
    var collection = property.GetValue(this) as IEnumerable<ModelRun>;
    if (collection != null)
    {
        foreach (var item in collection)
        {
            if (item == null) continue;
            var itemType = item.GetType();
            writer.WriteStartElement("ModelRun");
            // 写入元素实际类型的全名,用于反序列化时识别
            writer.WriteAttributeString("type", itemType.FullName);
            // 为当前元素的实际类型创建XmlSerializer
            var itemSerializer = new XmlSerializer(itemType);
            itemSerializer.Serialize(writer, item);
            writer.WriteEndElement();
        }
    }
    writer.WriteEndElement();
}

对应的反序列化(ReadXml方法)逻辑:

if (reader.Name == "ModelRuns" || reader.Name == "SavedModelRnuns")
{
    var collection = new ObservableCollection<ModelRun>();
    while (reader.Read())
    {
        if (reader.NodeType == XmlNodeType.Element && reader.Name == "ModelRun")
        {
            var typeName = reader.GetAttribute("type");
            var itemType = Type.GetType(typeName);
            if (itemType != null)
            {
                var itemSerializer = new XmlSerializer(itemType);
                var item = (ModelRun)itemSerializer.Deserialize(reader.ReadSubtree());
                collection.Add(item);
            }
        }
        else if (reader.NodeType == XmlNodeType.EndElement && (reader.Name == "ModelRuns" || reader.Name == "SavedModelRnuns"))
        {
            break;
        }
    }
    // 将反序列化后的集合赋值给对应属性
    property.SetValue(this, collection);
}

方法3:XmlInclude特性(仅适用于已知派生类型)

若提前知晓所有派生类型,可在ModelRun类上添加[XmlInclude]特性,但此方法无法处理插件动态引入的未知类型,仅作补充:

[XmlInclude(typeof(ScenarioModelRun))]
public class ModelRun : INotifyPropertyChanged
{
    // 类实现代码
}

内容的提问来源于stack exchange,提问作者FunkyDexter

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.16 09:20:30