3

假设我想将一些 xml 解析成一个强类型的类。当我得到xml时,我不知道它应该是A型还是B型,直到我打开它并查看它。我可以看一下,并返回一个像这样的枚举:

BaseType x = null;
TypeInfoEnum typeInfo = BaseType.GetTypeInfo(xml);

if(typeInfo == TypeInforEnum.TypeA)
{
    x = BaseType.ParseXmlToTypeA(xml);

    // do other work on Type A
}

else if(typeInfo == TypeInfoEnum.TypeB)
{
    x = BaseType.ParseXmlToTypeB(xml);

    // do other work on Type B
}

或者我可以只用一种方法处理解析并检查类型:

BaseType x = BaseType.ParseXml(xml);

if(x.GetType() == typeof(TypeA))
{
    // do work on Type A
}
else if(x.GetType() == typeof(TypeB))
{
    // do work on Type B
}

只是想从您喜欢的设计角度获得其他人的想法。现在,细节不是很重要。我只是根据 xml 中的内容从单个 XML 源创建 2 种不同的类型。没什么复杂的。

更新:

感谢您到目前为止的答案。类型在这里并不重要,但作为示例,类层次结构可能如下所示:

class BaseType
{
    public string CommonData { get; set; }
}

class TypeA : BaseType
{
    public string TypeASpecificData { get; set; }
}

class TypeB : BaseType
{
    public string TypeBSpecificData { get; set; }
}

由于此功能将被集成到其他人将使用的程序集中,因此我喜欢使用 Enum 的第一个选项,因为让 API 的用户检查某物的类型似乎很尴尬,即使用 Enum 在语义上似乎更彻底。

4

5 回答 5

6

在第一个选项中,您实际上是在复制信息(类型 + 枚举)而没有明显的好处。因此,鉴于这两个选项,我会选择第二个,尽管我更喜欢更惯用is的而不是GetType比较:

BaseType x = BaseType.ParseXml(xml);

if(x is TypeA)
{
    // do work on Type A
}
else if(x is TypeB)
{
    // do work on Type B
}

但是,您可以考虑第三种选择:

BaseType x = BaseType.ParseXml(xml);
x.DoWork();

DoWork作为 BaseType 的抽象方法,在 TypeA 和 TypeB 中被覆盖:

public abstract class BaseType
{
    public abstract void DoWork();
}
public class TypeA : BaseType
{
    public override void DoWork() {
        // do work on Type A
    }
}
public class TypeB : BaseType
{
    public override void DoWork() {
        // do work on Type B
    }
}
于 2013-07-09T19:24:52.847 回答
3

您需要做的是拥有 2 种不同的方法 - 一种处理 A 型,另一种处理 B 型:

public void DoWork(A a) { .. }

public void DoWork(B b) { .. }

然后你只需将实例发送到doWork. 这将导致您的代码在没有任何类型检查的情况下准确地完成需要完成的工作:

BaseType x = BaseType.ParseXml(xml);
DoWork(x);

另一种选择是在两个类中实现 DoWork 方法:

 public abstract class BaseType {
    public abstract void DoWork();
 }

 public class A: BaseType { 

    public void DoWork() { ... }
 }

 public class B: BaseType { 

    public void DoWork() { ... }
 }

然后您的解析将如下所示:

BaseType x = BaseType.ParseXml(xml);
x.DoWork();
于 2013-07-09T19:27:51.453 回答
2

我通常这样做:

1:定义某种键,例如您已经建议:

TypeInfoEnum typeInfo
{
...
}

2:使用声明如下的解析器创建字典:

Dictionary<TypeInfoEnum, Func<XDocument, IBase>>

3:像这样实现您的公共方法:

public class Parser
{
    IBase Parse(XDocument xDocument)
    {
        TypeInfoEnum key = GetKeyForXDocument(xDocument);
        IBase x = DictionaryWithParsers[key](xDocument);

        return x;
    }
}

我忽略了错误处理和 GetKeyForXDocument 方法的实现,但这应该不是很困难。

您的 API 使用者会像这样使用它:

void SomeConsumingMethod()
{
    ...

    IBase x = serviceObject.Parse(xDocument);

    // Members declared in IBase:
    x.SomeMethod();

    // Members declared in ITypeA or ITypeB
    if (x is ITypeA)
        ((ITypeA)x).A();

    if (x is ITypeB)
        ((ITypeB)x).B();
}
于 2013-07-09T19:23:54.320 回答
1

我相信这样的事情会奏效,看起来更简单。

[XmlInclude(typeof(TypeA))]
[XmlInclude(typeof(TypeB))]
class BaseType
{
    public string CommonData { get; set; }
}

class TypeA : BaseType
{
    public string TypeASpecificData { get; set; }
}

class TypeB : BaseType
{
    public string TypeBSpecificData { get; set; }
}

并反序列化:

var serializer = new XmlSerializer(typeof(BaseType));
BaseType result;

using (TextReader reader = new StringReader(xmlString))
{
    result = (BaseType)serializer.Deserialize(reader);
}
于 2013-07-09T19:53:37.840 回答
0

关注点分离的构建

我看到违反单一责任原则 (SRP),因为 XML 输入的解析是在该类中完成的。

使用枚举启用 SRP 应用程序

...然后类 A、B、C 的构造与类本身或它们的基类分离。不要为了解析该枚举而定义基类。

给定一个有效值enum,将该枚举传递给工厂。

public static void main() {
    SomeClassFactory factory = new SomeClassFactory();

    // putting the parsing in the factory expresses the
    // association of the enum to its target types.
    SomeClassEnum someClassName = SomeClassFactory.Parse(xmlInput);
    BaseType someClassInstance = factory.Create(someClassName);
}

public class SomeClassFactory{
    // Potentially throws NotImplementedException
    public static SomeClassEnum Parse (string xmlInput) { ... }

    // the type is already resolved, so we don't need to do
    // it again in any of the code called herein.
    public SomeClassBase Create (SomeClassEnum thisClassName) {
        BaseType newInstance;

        switch(thisClassName) { 
            case SomeClassEnum.TypeA:
                newInstance = buildTypeA();
                break;
            // ...
        }

        return newInstance;
    }
}
于 2015-12-17T19:19:57.410 回答