C# 使用奇怪的循环模板模式继承序列化

C# 使用奇怪的循环模板模式继承序列化,c#,design-patterns,generics,xml-serialization,xml-deserialization,C#,Design Patterns,Generics,Xml Serialization,Xml Deserialization,我有一个基类,定义如下: public abstract class XMLBackedObject<T> where T: XMLBackedObject<T> { /// <summary> /// Load the specified xml file and deserialize it. /// </summary> /// <param name='filePath'> /// File path to load //

我有一个基类,定义如下:

public abstract class XMLBackedObject<T> where T: XMLBackedObject<T>
{   
/// <summary>
/// Load the specified xml file and deserialize it.
/// </summary>
/// <param name='filePath'>
/// File path to load
/// </param>
public static T Load(string filePath)
{
    XmlSerializer serializer = new XmlSerializer(typeof(T));
        using(FileStream stream = new FileStream(filePath, FileMode.Open))
        {
            return serializer.Deserialize(stream) as T;
        }
}

/// <summary>
/// Save this instance to the specified file path
/// </summary>
/// <param name='filePath'>
/// File path to save to.
/// </param>
public void Save(string filePath)
{
    XmlSerializer serializer = new XmlSerializer(typeof(T));
        using(FileStream stream = new FileStream(filePath, FileMode.Create))
        {
            serializer.Serialize(stream, this);
        }
}
}
我只需要MenunigationCommand保存它继承的命令类中存在的字段,而不是MenunigationCommand中的任何新字段。有没有办法做到这一点?或者我应该在使用多个继承级别的每个类上实现一个Load and Save方法吗


编辑:添加文件的完整源代码。

MenuChoiceCommand
继承
命令
,该命令继承
XMLBackedObject
,而不是
XMLBackedObject
。因此,由
Save
创建的序列化程序用于类型
Command
,而不是
MenuChoiceCommand
。。。您需要使
MenuChoiceCommand
inherit
XMLBackedObject
,才能使其工作(但是您将无法使其inherit
命令,因为C不允许多重继承)

乍一看,使用这种奇怪的重复模板模式似乎是个好主意,但正如您所看到的,您很快就会遇到它的局限性

无论如何,我不认为序列化逻辑应该是数据类本身的一部分;最好在具有泛型方法的帮助器类中执行此操作:

public static class XmlHelper
{
    public static T Load<T>(string filePath)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        using(FileStream stream = new FileStream(filePath, FileMode.Open))
        {
            return (T)serializer.Deserialize(stream);
        }
    }

    public static void Save<T>(T obj, string filePath)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        using(FileStream stream = new FileStream(filePath, FileMode.Create))
        {
            serializer.Serialize(stream, obj);
        }
    }
}
公共静态类XmlHelper
{
公共静态T加载(字符串文件路径)
{
XmlSerializer serializer=新的XmlSerializer(typeof(T));
使用(FileStream-stream=newfilestream(filePath,FileMode.Open))
{
返回(T)序列化程序。反序列化(流);
}
}
公共静态无效保存(T obj,字符串文件路径)
{
XmlSerializer serializer=新的XmlSerializer(typeof(T));
使用(FileStream-stream=newfilestream(filePath,FileMode.Create))
{
serializer.Serialize(流,obj);
}
}
}

我不确定从MenuNavigationCommand调用Save时出错的原因,它应该可以正常工作。你能把准确的输入信息公布出来吗?我也不确定你的问题到底是关于什么的:修复这个错误,并告诉你一种方法来区分Command和MenuNavigationCommand中的字段;或者仅仅是这个差异就意味着这个错误不再出现(!?)。谢谢,我已经为涉及的类添加了完整的源代码。我的问题只是关于修复错误,很抱歉,我无意混淆MenunigationCommands中有关字段的信息。我们在不同的时区,我无法更快地回来。Thomas Levesque还是解决了这个问题。在
Save
方法中使用
this.GetType()
而不是
typeof(T)
可能也会做正确的事情。但正如@ThomasLevesque所解释的,最好将此功能简单地移动到helper类中。感谢您的回答,我认为您是对的,helper方法看起来更好。假设,既然MenuChoiceCommand在字段方面与Command完全相同,我是否可以强迫MenuChoiceCommand对Command进行某种拳击,以便它仍然编写它?@NickUdell,对不起,我不明白你的意思。。。这和拳击有什么关系?哎呀,我是说选角!看来我需要更多的睡眠!我刚刚发现了XMLInclude属性,它似乎是处理问题的另一种方法,将派生类与类型一起序列化为基类,然后以这种方式反序列化。@NickUdell,好吧,从MenuChoiceCommand转换为Command不会有任何效果,因为MenuChoiceCommand已经是一个命令了。。。XmlInclude可以帮助您,但我仍然认为序列化逻辑无论如何不应该在这些类中。您是对的,我已经将逻辑移到了您建议的XmlHelper类中,非常感谢您的帮助。
using UnityEngine;
using System.Collections.Generic;

[System.Serializable]
public abstract class Command : XMLBackedObject<Command>
{
    //The word that triggers this command
    public Word Word;
    //The command's target
    public List<Word> Targets;
    //Minimum number of targets for the command to be valid
    public int RequiredTargets;

    //Message to send when bad targets are supplied
    public string BadTargetString;
    //Message to send when no target is supplied
    public string noTargetString;


    public Command(Word word, List<Word> targets,int requiredTargets)
    {
        Targets = targets;
        this.Word = word;
        this.RequiredTargets = requiredTargets;
    }

    public Command()
    {
        Targets = new List<Word>(); 
    }

    /// <summary>
    /// Execute the command on the supplied targets
    /// </summary>
    /// <param name='targets'>
    /// Targets to process
    /// </param>
    public abstract void Execute(IEnumerable<Word> targets);
}
using UnityEngine;
using System.Collections;
using System.Collections.Generic;

[System.Serializable]
public class MenuChoiceCommand : Command {

    public MenuChoiceCommand()
    {
    }

    public MenuChoiceCommand(Word word, List<Word> targets, int requiredTargets) : base(word,targets,requiredTargets)
    {
    }

    public override void Execute (System.Collections.Generic.IEnumerable<Word> targets)
    {

    }
}
public void BuildTestXMLFiles()
    {
        Config config = new Config();
        config.CommandDirectoryPath = "commdirpath";
        config.WordDirectoryPath = "wordirparth";
        config.Save (Application.dataPath + "/testconfig.xml");

        MenuChoiceCommand command = new MenuChoiceCommand(word,new List<Word>(),2);
        command.Targets.Add (word);
        command.Save (Application.dataPath + "/testcommand.xml");
    }
InvalidOperationException: The type of the argument object 'MenuChoiceCommand' is not primitive.
public static class XmlHelper
{
    public static T Load<T>(string filePath)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        using(FileStream stream = new FileStream(filePath, FileMode.Open))
        {
            return (T)serializer.Deserialize(stream);
        }
    }

    public static void Save<T>(T obj, string filePath)
    {
        XmlSerializer serializer = new XmlSerializer(typeof(T));
        using(FileStream stream = new FileStream(filePath, FileMode.Create))
        {
            serializer.Serialize(stream, obj);
        }
    }
}