2012-02-07 19 views
5

.net不允许在基类中实现部分接口。作为一种缓解措施,我已经选择了3种备选解决方案。请帮助我确定哪一个更适用于重构,编译/运行时错误,可读性。 但首先有几条评论。C#在基类/抽象类中的最佳部分接口实现

  • 当然,您可能总是将对象投射到IFoo并在没有任何编译器警告的情况下调用任何方法。但这不合逻辑,你不会那样做。这个构造不会因重构而发生。
  • 我想要最大的分离度。直接类合同(公共方法和属性)应该与接口实现分开。我使用很多接口来分离对象的交互。

我比较:

  1. BaseClass1/MyClass1的:
    • CON:有在BaseClass1创建虚拟抽象的IFoo的每个未实现的方法。
    • con:其他方法包装 - 运行时对生产力的影响很小。
  2. BaseClass2/MyClass2:
    • CON:没有编译器警告,如果在MyClass2没有实现方法2的。运行时异常。用单元测试覆盖较差的重构可能会破坏代码的稳定性。
    • con:必须放置额外的过时构造来防止子类中的直接方法调用。
    • con:Method2对于BaseClass1是公开的,所以它现在是类合同的一部分。必须放置“过时”构造以防止直接呼叫,而不是通过IFoo。
  3. BaseClass3/MyClass3:
    • 亲:(相较于#2)。更可读。你会发现MyClass2.Method2是IFoo的实现,而不仅仅是一些重载方法。
public interface IFoo 
{ 
    void Method1(); 
    void Method2(); 
} 
public abstract class BaseClass1 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    void IFoo.Method2() 
    { 
     IFooMethod2(); 
    } 

    protected abstract void IFooMethod2(); 
} 

public class MyClass1 : BaseClass1 
{ 
    [Obsolete("Prohibited direct call from child classes. only inteface implementation")] 
    protected override void IFooMethod2() 
    { 
     //some implementation 
    } 
} 
public abstract class BaseClass2 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    [Obsolete("Prohibited direct call from child classes. only inteface implementation")] 
    public virtual void Method2() 
    { 
     throw new NotSupportedException(); 
    } 
} 

public abstract class MyClass2 : BaseClass2 
{ 
    public override void Method2() 
    { 
     //some implementation 
    } 
} 
public abstract class BaseClass3 : IFoo 
{ 
    void IFoo.Method1() 
    { 
     //some implementation 
    } 

    void IFoo.Method2() 
    { 
     throw new NotSupportedException(); 
    } 
} 

public abstract class MyClass3 : BaseClass3, IFoo 
{ 
    void IFoo.Method2() 
    { 
     //some implementation 
    } 
} 
+6

这是一个**非常尴尬的模式,你试图实现。你说*“.NET不允许在基类中实现部分接口。”* - 这是有原因的。客户端代码需要某种**实现接口**,或许...... **实现接口**。对于不支持的方法抛出异常,当然是*非常*糟糕的代码异味... – Yuck 2012-02-07 12:47:47

+0

同意Yuck。如果你有一个'IFoo'类型的变量,你真的希望'IFoo'的所有方法都可以实现并且可用。接口是为了这个。 – ken2k 2012-02-07 12:51:32

+0

只有MyClass1 _必须完全实现接口。它确实如此。问题是有多个子类(我没有提及它),每个都必须实现IFoo。如果没有基类,您必须复制/粘贴Method1实现,这对所有子类都是相同的。这是我想要避免的。但是Method2实现在子类中是不同的,所以我不能只有一个实现Method1和Method2的类。 – user1194528 2012-02-07 13:11:56

回答

5

好吧,你可以试试下面的BaseClass是抽象:

public interface IFoo 
{ 
    void Method1(); 

    void Method2(); 
} 

public abstract class BaseClass : IFoo 
{ 
    public void Method1() 
    { 
     // Common stuff for all BaseClassX classes 
    } 

    // Abstract method: it ensures IFoo is fully implemented 
    // by all classes that inherit from BaseClass, but doesn't provide 
    // any implementation right here. 
    public abstract void Method2(); 
} 

public class MyClass1 : BaseClass 
{ 
    public override void Method2() 
    { 
     // Specific stuff for MyClass1 
     Console.WriteLine("Class1"); 
    } 
} 

public class MyClass2 : BaseClass 
{ 
    public override void Method2() 
    { 
     // Specific stuff for MyClass2 
     Console.WriteLine("Class2"); 
    } 
} 

private static void Main(string[] args) 
{ 
    IFoo test1 = new MyClass1(); 
    IFoo test2 = new MyClass2(); 

    test1.Method2(); 
    test2.Method2(); 

    Console.ReadKey(); 
} 
+0

这是我的变种#2,除了第一个缺点。仍然适用:1.必须添加额外的过时构造以防止来自子类的直接方法调用。在你的情况下,没有Obsolete,所以直接调用子类不会产生编译时警告。2. Method2是BaseClass1的公开方法,因此它现在是类合同的一部分。必须放置“过时”构造以防止直接呼叫,而不是通过IFoo。 3.可读性较差。你没有看到Method2是IFoo的实现。 – user1194528 2012-02-07 13:38:01

+0

阅读问题下面的评论,这似乎是解决问题的正确方法。在'BaseClass'中抽象实现'Method2()'确保'IFoo'完全实现,同时迫使所有从'BaseClass'派生的类实现'Method2()'。还可以补充说,派生类将无法重写'Method1()'(但它们将能够隐藏它(使用'new'关键字))。 – Nailuj 2012-02-07 13:41:04

+0

@ user1194528上述代码的实际问题是什么?你为什么要放弃已过时的注释? – ken2k 2012-02-07 13:43:09

6

这是非常糟糕的设计是没有按类没有一个明确的定义d合同。这是极端的,因为你首先说一个班级有能力做某事。你明确地强调类可以做的东西,但后来在你说的那个代码中,拧紧它,这个类可以在没有实现的情况下生存。编译器非常明智地要求您执行合同,但由您决定。

下面是一些常见的解决方案

不好解决

  • 抛出一个异常(NonImplementedException或NotSupportedException异常,见sample
  • 声明为过时的(设计它从一开始就很好)

更好解决方案

  • 显式接口实现,但你仍然实现它(只是一种隐藏)

最好的解决方案

  • 使用接口隔离(拆你的脂肪接口转换成更薄和更易于管理的)
+1

我想到了界面分离,但问题是界面设计来自客户端类需求,而不是服务器类。客户端类不想知道实现细节。为什么它应该知道,如果这个接口是在基类中实现的,还有其他的在儿童中呢?它也非常脆弱,可能有两个类层次结构实现了这个接口,它们在实现在基类和子类之间的分离方式上有所不同。 – user1194528 2012-02-07 14:13:37

0

我建议具有抽象基类实现接口调用protected abstract方式方法,如在第一个例子中,除了一些派生类可能无法实现方法(继“抛开一切到IList但没有所有的方法实际上工作“模式);那些可能是protected virtual存根扔NotSupportedException

注意,它是由子类是否对接口的任何特定成员公开为名称相同的公共成员(可调用相应的抽象成员)。

在VB.net中的正确模式将类似MustOverride Sub IFoo_Method1() Implements IFoo.Method1,这将避免额外的函数调用开销,但C#没有提供任何实现与受保护成员接口的方法。对于可能必须在子类中重写的任何方法使用显式接口实现有点难以实现,因为孩子的接口的重新实现不可能链接到父实现。

+0

>>孩子重新实现接口链接父母的实现是不可能的。 - 好点子。从这个角度来看,最好将所有方法声明为受保护的(+为那些抽象,未实现)(ken2k变体)。谢谢你们所有的想法。现在我可以做出更明智的决定。 – user1194528 2012-02-08 17:37:08

8

我喜欢这个版本,基类不能被实例化,因为它的抽象,派生类必须列出的IFoo在其声明中,否则它将不会被实现接口,然后是实施其余全权负责的界面。 一个缺点我能看到的是你不能明确地实现在基类的接口方法(即没有的IFoo:方法),但除此之外,这是一个相当低开销的版本。

public interface IFoo 
{ 
    void Method1(); 
    void Method2(); 
} 

public abstract class BaseClass1 
{ 
    public void Method1() 
    { 
     //some implementation 
    } 
} 

public class MyClass1 : BaseClass1, IFoo 
{ 
    public void Method2() 
    { 
     //some implementation 
    } 
}