2012-12-05 41 views
4

我们假设以下具有基类BC和2个派生类DC_A和DC_B的类结构; 此外还有一类XY具有梅索德咕()与BC类型的参数和其他methodes如何避免'如果级联'和使用多态性类型转换?

// base class 
public class BC 
{ 
    public virtual void foo(); 
} 

// derived class A 
public class DC_A : BC 
{ 
    public override void foo() {} 
} 

// derived class B 
public class DC_B : BC 
{ 
    public override void foo() {} 
} 


public class XY 
{ 
    public void goo(BC o) 
    { 
     // perfectly fine using polymorphism; no Ifs' no casting, OOP at its best ;-) 
     o.foo(); 

     // but what to do here? 
     if ((o as DC_A) != null) 
     { 
      doX(o as DC_A); 
     } 
     else if ((o as DC_B) != null) 
     { 
      doY(o as DC_B); 
     } 
    } 

    private void doX(DC_A o) {} 
    private void doY(DC_B o) {} 
} 

在“Is passing around value objects using polymorphism a bad practice?”访问者模式提出的;

的级联如果的问题和铸造移动(未消除)到抽象基类。

有没有更好的解决方案,以完全避免,如果的?

它没有选择,我(在这个例子中)移动的功能从DOX/DOY类DC_A/DC_B

你的意见是非常赞赏。

编辑: 此问题的背景是一个带有窗体的C#/ WinForms应用程序,用于管理由不同子实体组成的“测试规则”,包括测量类型,测试限制等的集合DC_A,DC_B类)全部从EntityBase派生(=来自上面的BC) 表格向控制器发送一个事件,表明子实体已经改变; 简化PropertiesChanged(EntityBase O) 控制器调用模型中的类对应的梅索德(从上方类XY梅索德咕),其现在负责做这不仅持续改变的子实体测试极限,而且例如在businesslogic创建一个新的测试极限值修订的对象,增加了测试规范的修订等。 也许这样 具有类似于PropertiesChanged(EntityBase O)A梅索德的通用方法应改为从形式和具体的事件处理程序更specifing事件在控制器和模型类来处理“TestLimitChanged”等

不过这种特殊情况,导致我多态更通用的或“哲学”的问题在所有;-)

+0

如果DOX和DOY工作非常不同,但应在同一地点被称为逻辑,唯一错了你的做法是,你应该委托艇员选拔的责任,这在其他地方使用。 –

回答

5

如果您使用的是.NET 4,那么可能会出现重载和动态类型,也许这是您的另一种选择。

class Program 
{ 
    static void Main(string[] args) 
    { 
     DC_A dca = new DC_A(); 
     DC_B dcb = new DC_B(); 
     XY xy = new XY(); 
     xy.goo(dca); 
     xy.goo(dcb); 
    } 
} 

// base class 
public abstract class BC 
{ 
    public abstract void foo(); 
} 

// derived class A 
public class DC_A : BC 
{ 
    public override void foo() { } 
} 

// derived class B 
public class DC_B : BC 
{ 
    public override void foo() { } 
} 


public class XY 
{ 
    //public void goo<T>(T o) where T : BC 
    //{ 
    // //dynamic dyn = Convert.ChangeType(o, o.GetType()); 
    // //dynamic dyn = o; 
    // //gooi(dyn); 
    // gooi((dynamic)o); 
    //} 

    // http://smellegantcode.wordpress.com/2008/11/04/dynamic-typing-in-c-4-and-duck-generics/ 
    public void goo<T>(T o) where T : BC 
    { 
     gooi((dynamic)o); 
    } 

    private void gooi(DC_A o) 
    { 
     o.foo(); 
     doX(o); 
    } 

    private void gooi(DC_B o) 
    { 
     o.foo(); 
     doY(o); 
    } 

    private void gooi(BC o) 
    { 
     o.foo(); 
    } 

    private void doX(DC_A o) { } 
    private void doY(DC_B o) { } 
} 
+0

为了加快代码替换“dynamic dyn = Convert.ChangeType(o,o.GetType());”与“动态dyn = o;” – eisentor

+0

那就是我想要做的,完美!在.NET(动态类型)和OOP(重载)的帮助下,不再需要if和cast。感谢这个解决方案 – lampi

3

我的建议:

  1. 公开您的doX()和doY()方法,以便它们可以被cal由DC_A和DC_B的实现引导。
  2. 向BC添加抽象Do()方法。
  3. 在DC_A中实现Do()以调用公开的doX()方法。
  4. 在DC_B中实现Do()以调用公开的doY()方法。
  5. Inside goo(),只需调用o.Do()。

您可以通过将委托传递给它们将调用的DC_A和DC_B而不是暴露doX()和doY()来进一步分离事物。它非常依赖于使用模式。

然而,这仅是真的非常有用,如果你能在派生类中的某些值添加到DO()实现。如果所有的Do()都只是调用doX()或doY(),那么这样做并没有真正的好处。

所以我要在这里跟其他的海报,上面写着只是做铸造同意。

+0

非常感谢您的建议;但使一些事情变得复杂.. 如果doX/doY不仅仅取决于对象DC本身,还取决于BC(DC_A,DC_B)不能(应该)知道的其他参数等等。 例如doX(o如DC_A,otherPara1,otherPara2,DBconnectionPara3 ..)? – lampi

+0

在这种情况下,我认为你需要考虑doX()和doY()实际上在做什么。他们正在使用的DC_A和DC_B的额外位数是多少?很难从一个广义的例子中看出,例如这个线程中的例子。例如,如果其他人创建DC_C类并将其传入,那么您希望发生什么?你如何确保DC_C的实施者知道他们也应该在课堂上做某些事情?你能提供更多的细节吗?但是,如果它太复杂了,你可能只是做你已经做的事情。 –

2

我不认为有太多的担心,一旦我们解决一两件事情:

  • 此刻你铸件超过你需要
  • 那些if只有一个S能是真实的,所以你并不需要一个else

在那之后,我们有:

// perfectly fine using polymorphism; no Ifs' no casting, OOP at its best ;-) 
    o.foo(); 

    var dca = o as DC_A; 
    if (dca != null) 
    { 
     doX(dca); 
    } 

    var dcb = o as DC_B; 
    if (dcb != null) 
    { 
     doY(dcb); 
    } 

我会说,看起来不错。

如果你想走得更远,你可以改变的doXdoY的实现,使他们如果通过null立即返回;那么你可以说

doX(o as DC_A); 
doY(o as DC_B); 
+0

你是对的;调用doX(o为DC_A​​)并在doX中检查NULL指针是一种方法; 它将投射移动到fucntion调用和IF检查到doX避免讨厌的if-then-else级联; 但它并不能避免ifs和cast。 – lampi