代码之家  ›  专栏  ›  技术社区  ›  Chris Laplante

如何解决这个C#多态性问题?

  •  0
  • Chris Laplante  · 技术社区  · 14 年前

    我有一个基类, SpecialClass . 很多其他类从中继承,比如 WriteSpecialClass ReadSpecialClass . 这些类的实例在被强制转换到基类或从基类强制转换到基类后被序列化和反序列化。因此,我有很多这样的重复代码(用于序列化):

    SpecialClass sc = null;
    
    if (type.DisplayName == "Read") {
        sc = new ReadSpecialClass();
    } else if (type.DisplayName == "Write") {
        sc = new WriteSpecialClass();
    } else
        throw new NotSupportedException("Type not supported");
    
    String xml = SerializeToXML(sc);
    

    .. 以及反序列化:

    SpecialClass sc = null;
    
    sc = (SpecialClass)DeserializeFromXML(xml);
    
    switch (someVariableThatDicatesTypeOfSpecialClass) {
        case "Read":
            sc = (ReadSpecialClass)sc;
            break;
        case "Write":
            sc = (WriteSpecialClass)sc;
            break;
    }
    

    还有一条重要信息: 特殊类别 定义了一系列抽象方法,每个从中继承的类都需要实现这些方法。所有从它继承的类都符合方法,但返回的内容不同,因此每个类都不相同。

    如果需要,我可以发布序列化或反序列化方法。

    我想试着简化一下,这样我就不必详细说明每一个 特殊类别 -派生类(如 ReadSpecialClass类 ). 有办法吗?我唯一能想到的办法就是打字。谢谢你的帮助,

    5 回复  |  直到 14 年前
        1
  •  2
  •   cdhowie    14 年前

    对于序列化,可以使用某种查找:

    public class SpecialClass
    {
        private static Dictionary<string, Func<SpecialClass>> factories =
            new Dictionary<string, Func<SpecialClass>>();
    
        static SpecialClass()
        {
            factories["Read"] = () => new ReadSpecialClass();
            factories["Write"] = () => new WriteSpecialClass();
        }
    
        public static SpecialClass CreateByName(string name)
        {
            Func<SpecialClass> factory;
    
            if (!factories.TryGetValue(name))
                throw new ArgumentException("name", "\"" name +
                    "\" is not a recognized subclass of SpecialClass.");
    
            return factory();
        }
    }
    

    对于反序列化,这两行:

    sc = (ReadSpecialClass)sc;
    sc = (WriteSpecialClass)sc;
    

    不执行实际转换。他们唯一要做的就是在 sc 不是适当的类型。你在这里所做的和:

    object a = "foo";
    a = (string)a;
    

    当然,演员会成功的。但它不会以任何方式修改 a . 它真正做的就是验证这个对象已经是一个字符串。

        2
  •  3
  •   Rob    14 年前

    在SpecialClass中考虑过serialize()方法吗?这样,如果有一些特殊的注意事项,可以重写base.serialize方法并处理任何唯一的需要。这样,它就已经知道它是什么了,并且不需要条件。

    另一件需要考虑的事情可能是一个带有facade模式的helper类。然后可以有一个名为“public SpecialClass DeserializeSpecialClass()”的方法。然后,不必在反序列化程序中强制类型转换,而是可以在目标上强制类型转换。如果你发现你做了太多的转换,那么可以考虑将抽象方法添加到基类中,这个基类将在派生类中实现。

    希望这有帮助。

        3
  •  0
  •   Tokk    14 年前
    if (sc is WriteSpecialClass)
    {
        sc = (WriteSpecialClass) sc;
    }
     else if (sc is ReadSpecialClass)
    {
        sc = (ReadSpecialClass) sc;
    }
    else
    {
        throw new NotSupportetException("Type not Supportet");
    }
    
        4
  •  0
  •   Shuo    14 年前

    如果不关心性能,可以使用反射来初始化其名称包含该类型名称的类型。

    SerializeToXML() 占了基类 SpecialClass 作为参数,所以它不应该区分派生类之间的差异 ReadSpecialClass WriteSpecialClass . 你为什么不 序列化XML() 作为基类的实例方法?

        5
  •  0
  •   Tim Jarvis    14 年前

    规则1./派生类应该负责序列化和反序列化它们自己,因为它们应该是唯一对存储在XML文档中的其他数据有深入了解的类。

    所以从多形性的角度(注意上面的代码不是多态的)您可以做如下的事情

    public class SpecialClass
    {
      public virtual XElement SerializeToXML()
      {
        // Base impl
      }
    }
    
    public class YourDerivedClasses
    {
      public override XElement SerializeToXML()
      {
        //derived implementation 
      }
    }
    

    相当直接。但问题不在于序列化或多态行为,而在于从XML中实例化正确的类型。

    解决这个问题的一种方法是在XML文档中有一个指定保存它的类型的键,并注册一个负责构造由该键索引的派生类型的工厂(属性有利于这种注册),一旦构造了派生类型,就使用它来反序列化XML。

    注意,您的工厂也可以是基类上的静态方法。

    比如(当然是未经测试的)。。。。

    public class SpecialClass
    {
      ***snip
      public static IEnumerable<SpecialClass> GetAllClasses(XElement xml)
      {
        IDictionary keyedtypes = GetKeyedTypesDictUsingReflection() // the registered factories
        foreach(var x in xml.Elements("YourClassesNode"))
        {
          string key = //get key from xml
          yield return keyedTypes[key].DeserializeFromXML(x);
        }
      }
    }