C#在基类/抽象类中的最佳部分接口实现

本文关键字:最佳部 接口 实现 基类 抽象类 | 更新日期: 2023-09-27 18:23:42

.net不允许在基类中实现部分接口。作为缓解措施,我提出了3种替代解决方案。请帮助我决定在重构、编译/运行时错误和可读性方面哪个更通用。但首先是几条评论。

  • 当然,您可能总是将对象强制转换为IFoo,并在没有任何编译器警告的情况下调用任何方法。但这不符合逻辑,你通常不会这么做。这个构造不会因为重构而出现
  • 我想要最大的间距。直接类约定(公共方法和属性)应该与接口实现分开。我经常使用接口来分隔对象交互

我的比较:

  1. BaseClass1/MyClass1:
    • con:必须在BaseClass1中为IFoo的每个未实现的方法创建虚拟抽象
    • con:额外的方法包装-在运行时对生产力有轻微影响
  2. BaseClass2/MyClass2:
    • con:如果MyClass2中没有Method2的实现,则没有编译器警告。运行时异常。单元测试覆盖率低的重构可能会破坏代码的稳定
    • con:必须放置额外的过时构造,以防止从子类直接调用方法
    • con:Method2对于BaseClass1是公共的,所以它现在是类约定的一部分。必须放置"Obsolete"构造以防止直接调用,而不是通过IFoo
  3. BaseClass3/MyClass3:
    • pro:(与#2相比)。可读性更强。您可以看到MyClass2.Method2是IFoo实现,而不仅仅是某个overriden方法
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
    }
}

C#在基类/抽象类中的最佳部分接口实现

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

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
    }
}

好的,您可以尝试以下操作,因为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();
}

设计一个没有实现定义良好的契约的类是非常糟糕的。这是极端的,因为你首先说一个班有能力做某事。您明确强调了类可以做的事情,但在代码的后面,您说nahh,见鬼去吧,这个类可以在没有实现的情况下生存。编译器非常明智地要求您执行合同,但这由您自己决定。

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

错误的解决方案

  • 引发异常(NonImplementedException或NotSupportedException,请参阅示例)
  • 宣布它过时(从一开始就设计好)

更好的解决方案

  • 显式接口实现,但您仍然实现它(只是有点隐藏它)

最佳解决方案

  • 使用界面分离(将脂肪界面分割成更薄、更易于管理的界面)

我建议让抽象基类用调用protected abstract方法的方法实现接口,如您的第一个示例所示,除了一些派生类可能无法实现的方法(遵循"将所有东西都放入IList,但并不是所有方法都能实际工作"模式);这些可以是抛出CCD_ 5的CCD_。

请注意,是否将接口的任何特定成员公开为类似名称的公共成员(可以调用适当的抽象成员)取决于子类。

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