创建可扩展属性类(OOP)

时间:2009-07-01 08:57:25

标签: c# oop inheritance xml-serialization

我有一个应用程序支持某些设备的多种类型和版本。它可以连接到这些设备并检索各种信息。

根据设备的类型,我有(除其他外)一个可以包含各种属性的类。某些属性对所有设备都是通用的,有些属性对于特定设备是唯一的。

此数据已序列化为xml。

在这些设备的未来版本中实现支持未来属性的类以及向后兼容以前的应用程序版本的首选方法是什么?

我可以想到几种方法,但我发现它们都不是很好:

  • 使用一组名称 - 值对:
    • 专业人员:良好的向后兼容性(我的应用的xml和以前版本)和可扩展性,
    • 缺点:没有类型安全,没有智能感知,需要实现自定义xml序列化(处理不同的value个对象)
  • 为每个新设备创建派生属性类:
    • 专业人员:输入安全
    • 缺点:必须使用XmlInclude或自定义序列化来反序列化派生类,不能与以前的xml架构向后兼容(尽管通过实现自定义序列化,我可以跳过未知属性?),需要强制转换才能访问派生类中的属性。
  • 另一种方法吗?

顺便说一句,我正在使用C#。

6 个答案:

答案 0 :(得分:2)

PropertyBag类似的内容怎么办?

答案 1 :(得分:1)

如果您不仅限于与外部架构的互操作性,那么您应该使用Runtime Serialization和SoapFormatter。运行时序列化的模式允许派生类指定需要序列化哪些属性以及在反序列化时如何处理它们。

XML Serializer需要XmlInclude,因为它实际上需要定义要使用的模式。

答案 2 :(得分:1)

我喜欢这种事情的名称/价值集。

可以处理许多缺点 - 考虑一个基类,它充当通用名称/值集,使用无操作方法验证传入的名称/值对。对于已知的名称集(即键),您可以创建实现验证方法的派生类。

例如,Printer可能有一个只能是“true”或“false”的已知键“PrintsColor”。如果有人试图加载PrintsColor =“CMYK”,您的Printer类将抛出异常。

根据您正在做的事情,您可以采用几种不同的方式使验证更方便 - 基类中的实用程序方法(例如checkForValidBoolean())或接受名称/类型信息的基类在其构造函数中,可以在派生类中使用更清晰的代码,也可以使用大多数自动化的XML序列化。

对于intellisense - 您的派生类可以具有根据键查找实现的基本访问器。 Intellisense将显示那些访问者名称。

这种方法对我来说效果很好 - 对于经典的OO设计有一种短视,特别是对于带有插件的大型系统。 IMO,这里的笨重类型检查是一个很大的拖累,但灵活性使它值得。

答案 3 :(得分:0)

我认为创建派生属性是最佳选择。

您可以使用xml架构设计新类。然后使用xsd.exe生成类代码。

使用.net并不难开发一个可以对xml进行序列化和反序列化所有类型的泛型类。

public static String toXmlString<T>(T value)
{
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));
    StringWriter stringWriter = new StringWriter();
    try { xmlSerializer.Serialize(stringWriter, value); }
    catch (Exception e)
    {
        throw(e);
    }
    finally { stringWriter.Dispose(); }
    String xml = stringWriter.ToString();
    stringWriter.Dispose();
    return xml;
}

public static T fromXmlFile<T>(string fileName, Encoding encoding)
{
    Stream stream;
    try { stream = File.OpenRead(fileName); }
    catch (Exception e)
    {

      e.Data.Add("File Name", fileName);
      e.Data.Add("Type", typeof(T).ToString());
      throw(e);
    }

    BufferedStream bufferedStream = new BufferedStream(stream);
    XmlSerializer xmlSerializer = new XmlSerializer(typeof(T));

    TextReader textReader;
    if (encoding == null)
        textReader = new StreamReader(bufferedStream);
    else
        textReader = new StreamReader(bufferedStream, encoding);

    T value;
    try { value = (T)xmlSerializer.Deserialize(textReader); }
    catch (Exception e)
    {
        e.Data.Add("File Name", fileName);
        e.Data.Add("Type", typeof(T).ToString());
        throw(e);
    }
    finally
    {
        textReader.Dispose();
        bufferedStream.Dispose();
    }
    return value;
}

答案 4 :(得分:0)

以编程方式说,这听起来像是Decorator Pattern的工作。从本质上讲,您有一个超类,它为所有这些类型的设备定义了一个通用接口。然后你有装饰器类,它们具有设备可能具有的其他属性。而且,在创建这些设备时,您可以动态添加这些装饰以定义设备的新属性。图形:

alt text

您可以查看维基百科页面以获取更详细的说明。在那之后,只需要一些序列化来告诉程序加载装饰器。

答案 5 :(得分:0)

你在这里想要完成的一般想法恰恰是EAV模式解决的问题。 EAV是数据库开发中最常用的模式,但该概念对应用程序同样有效。