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

本文关键字:枚举 类型 检查 更好 什么 | 更新日期: 2023-09-27 18:21:46

假设我想将一些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似乎在语义上更彻底。

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

在第一个选项中,您实际上是在复制信息(类型+枚举),而没有明显的好处。因此,考虑到这两个选项,我会选择第二个,尽管我更喜欢更惯用的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
    }
}

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

我通常喜欢这样做:

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();
}

我相信这样的东西会起作用,看起来更简单。

[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);
}

关注点分离的构建

我看到违反了单一责任原则(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;
    }
}