2013-07-09 46 views
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种不同的类型。没什么复杂。

UPDATE:

感谢迄今答案。该类型是不是真的这里很重要,但作为一个例子,类层次结构可能是这样的:

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

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

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

由于此功能将被卷成组件别人会用,我用喜欢的第一个选项枚举,因为让API的用户检查某些东西的类型看起来很尴尬,例如使用Enum在语义上看起来更彻底。

+0

您能否包含TypeA和TypeB类可能的外观示例? – EkoostikMartin

+0

只是一个错字我猜,但仍然: \t if(typeInfo == TypeInforEnum.TypeA)一个r太多 – Master117

+0

@ Master117太多了什么? – EkoostikMartin

回答

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是碱基类型的一个抽象方法,其在类型A和类型B覆盖:

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

对于第三种选择要谨慎的一件事是,作为TypeX实例的责任,DoWork是否有意义。在某些情况下,可能会有这种情况发生,但在很多情况下,采取这种方法会导致持续增加与阶级等级无关的责任。 –

+0

@DanBryant:非常好。这正是我将其作为“另一种选择”而不是“理想解决方案”的原因。我只是没有描述为什么...... – Heinzi

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

我认为这对于一些场景来说是一个很好的解决方案,但是在我的具体用法中,用户只会得到TypeA或TypeB,他们不能从xml中随意提取。 xml的来源总是相同的,但其中包含的内容是A或B. – Didaxis

+0

在调用方法之前,消费者是否知道将返回哪种类型? – lightbricko

+0

不,他们不会。他们基本上必须沿着路径A或路径B取决于XML中的内容。现在,实际上,TypeA和TypeB之间存在〜10个共同字段,每个字段约有30个非共同字段,这就是为什么我希望它们首先是不同的对象。基本上,你打开XML,并根据里面的内容,沿着A路径或B路径走下去。 – Didaxis

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

第一个例子只适用于[将x转换为'dynamic'](http://stackoverflow.com/questions/16326386/using-dynamic-in-c-sharp-首先让编译器生成适当的类型比较和动态分派。如果您直接传递基类型,它将尝试在编译时解析匹配并失败。 –

+0

好的。这取决于发生反序列化的方式。但基本上,你是对的。 – Avi

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

建设与关注

我看到违反单一职责原则(SRP)的分离,因为XML输入的解析是在那个班上完成。

使用枚举启用SRP应用

...然后classses 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; 
    } 
}