C# 什么';更好吗?枚举还是类型检查?

C# 什么';更好吗?枚举还是类型检查?,c#,.net,oop,design-patterns,types,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 }

假设我想将一些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
}
class BaseType
{
    public string CommonData { get; set; }
}

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

class TypeB : BaseType
{
    public string TypeBSpecificData { get; set; }
}
Dictionary<TypeInfoEnum, Func<XDocument, IBase>>
public class Parser
{
    IBase Parse(XDocument xDocument)
    {
        TypeInfoEnum key = GetKeyForXDocument(xDocument);
        IBase x = DictionaryWithParsers[key](xDocument);

        return x;
    }
}
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();
}
或者我可以在一个方法中处理解析并检查类型:

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源创建了两种不同的类型。没什么复杂的

更新:

谢谢你迄今为止的回答。这里的类型并不重要,但作为示例,类层次结构可能如下所示:

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
}
class BaseType
{
    public string CommonData { get; set; }
}

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

class TypeB : BaseType
{
    public string TypeBSpecificData { get; set; }
}
Dictionary<TypeInfoEnum, Func<XDocument, IBase>>
public class Parser
{
    IBase Parse(XDocument xDocument)
    {
        TypeInfoEnum key = GetKeyForXDocument(xDocument);
        IBase x = DictionaryWithParsers[key](xDocument);

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

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

我通常这样做:

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
}
class BaseType
{
    public string CommonData { get; set; }
}

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

class TypeB : BaseType
{
    public string TypeBSpecificData { get; set; }
}
Dictionary<TypeInfoEnum, Func<XDocument, IBase>>
public class Parser
{
    IBase Parse(XDocument xDocument)
    {
        TypeInfoEnum key = GetKeyForXDocument(xDocument);
        IBase x = DictionaryWithParsers[key](xDocument);

        return x;
    }
}
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();
}
1:定义某种类型的键,例如您已经建议的:

TypeInfoEnum typeInfo
{
...
}
2:使用声明如下的解析器创建字典:

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
}
class BaseType
{
    public string CommonData { get; set; }
}

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

class TypeB : BaseType
{
    public string TypeBSpecificData { get; set; }
}
Dictionary<TypeInfoEnum, Func<XDocument, IBase>>
public class Parser
{
    IBase Parse(XDocument xDocument)
    {
        TypeInfoEnum key = GetKeyForXDocument(xDocument);
        IBase x = DictionaryWithParsers[key](xDocument);

        return x;
    }
}
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();
}
我忽略了GetKeyForXDocument方法的错误处理和实现,但这应该不是很困难

您的API使用者会这样消费它:

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
}
class BaseType
{
    public string CommonData { get; set; }
}

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

class TypeB : BaseType
{
    public string TypeBSpecificData { get; set; }
}
Dictionary<TypeInfoEnum, Func<XDocument, IBase>>
public class Parser
{
    IBase Parse(XDocument xDocument)
    {
        TypeInfoEnum key = GetKeyForXDocument(xDocument);
        IBase x = DictionaryWithParsers[key](xDocument);

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

在第一个选项中,基本上是复制信息(type+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
是在TypeA和TypeB中重写的BaseType的抽象方法:

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

我相信这样做会奏效,似乎更简单

[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
值,将该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;
    }
}

你能举个例子说明TypeA和TypeB类可能是什么样子吗?我猜只是一个输入错误,但仍然是:if(typeInfo==TypeInforEnum.TypeA)一个rmany@Master117太多了什么?我会使用第一种方法,总是在解析之前检查以节省时间,而且我认为在这里切换会很好,但是我仍然有点困惑BaseType如何可以有两种不同的类型,就像第一个例子中一样。关于你的评论,我宁愿通过一个接口来查看:
if(myBaseTypeInstance是ITypeB){}
或等效的接口。至少这样,如果您有任何理由将
typespecificdata
TypeBSpecificData
组合到一个共享类中,您可以这样做。但也许这对你来说不是问题。我认为调用方检查反序列化类型没有什么不方便的原因(尽管调用方事先知道他们期望什么并利用泛型可能更有意义,但这可能超出了您的设计范围;如果更有意义,您可以更改)对于第三个选项,需要谨慎的一点是DoWork作为TypeX实例的责任是否有意义。在某些情况下,可能是这样,但在许多情况下,遵循这种方法会导致不相关的职责不断增加到类层次结构中。@DanBryant:这一点很好。这正是我将其作为“另一种选择”而不是“理想解决方案”提出的原因。我只是缺少文字来描述为什么……第一个示例只有在您第一次这样编译器才能生成适当的类型比较和动态调度的情况下才有效。如果您直接传递基类型,它将尝试在编译时解析匹配项,但失败了。这取决于反序列化的发生方式。但基本上,你是对的。我认为对于某些场景来说,这是一个很好的解决方案,但是在我的特定用法中,用户只能得到TypeA或TypeB,他们不能随意从xml中提取两者。xml的源总是相同的,但其中包含的是A或B。使用者在调用方法之前知道将返回什么类型吗?不知道。根据XML中的内容,它们基本上必须沿着路径A或路径B前进。实际上,TypeA和TypeB之间有10个公共字段,每个字段大约有30个非公共字段,这就是为什么我首先希望它们是不同的对象。基本上,你打开XML,根据里面的内容,你可以沿着A路径或者B路径,然后我会按照我最初的建议去做。我添加了一些示例代码。