什么更好?枚举或类型检查?

时间:2013-07-09 19:14:40

标签: c# .net oop design-patterns types

假设我想将一些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源创建两种不同的类型。没什么复杂的。

更新

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

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似乎在语义上更彻底。

5 个答案:

答案 0 :(得分: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
    }
}

答案 1 :(得分:3)

你需要做的是有两种不同的方法 - 一种处理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();

答案 2 :(得分: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();
}

答案 3 :(得分: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);
}

答案 4 :(得分:0)

分离关注的构建

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

使用enum启用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;
    }
}