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

时间:2012-02-07 12:26:45

标签: c# interface refactoring abstract-class

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

  • 当然,您可以始终将对象强制转换为IFoo,并在没有任何编译器警告的情况下调用任何方法。但这不符合逻辑,你不会这样做。这种结构不会因重构而发生。
  • 我想要最大限度的分离。直接类契约(公共方法和属性)应该与接口实现分开。我经常使用接口来分隔对象交互。

我的比较:

  1. BaseClass1 / MyClass1的:
    • con:必须在BaseClass1中为每个未实现的IFoo方法创建虚拟摘要。
    • con:附加方法换行 - 在运行时对生产力产生轻微影响。
  2. BaseClass2 / MyClass2:
    • con:如果在MyClass2中没有实现Method2,则不会发出编译器警告。而是运行时异常。单元测试覆盖率不佳的重构可能会破坏代码的稳定性。
    • con:必须添加额外的过时构造以防止从子类直接调用方法。
    • con:Method2对于BaseClass1是公共的,所以它现在是类合同的一部分。必须使用“过时”构造来防止直接调用,而不是通过IFoo。
  3. BaseClass3 / MyClass3:
    • 亲:(与#2相比)。更具可读性。您会看到MyClass2.Method2是IFoo实现,而不仅仅是一些覆盖方法。
  4. 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
        }
    }
    

4 个答案:

答案 0 :(得分:9)

我喜欢这个版本,基类不能实例化,因为它的抽象,派生类必须在其声明中列出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
    }
}

答案 1 :(得分:6)

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

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

糟糕的解决方案

  • 抛出异常(NonImplementedException或NotSupportedException,请参阅sample
  • 声明它已过时(从一开始就设计好)

更好的解决方案

  • 明确的界面实现,但你仍然实现它(只是隐藏它)

最佳解决方案

  • 使用界面隔离(将胖界面拆分为更薄且更易于管理的界面)

答案 2 :(得分: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();
}

答案 3 :(得分:0)

我建议让抽象基类使用调用protected abstract方法的方法实现接口,如第一个示例所示,除了某些派生类可能无法实现的方法(在“将所有内容输入之后) IList但是没有所有方法实际上工作“模式”;那些可能是protected virtual存根,它们会抛出NotSupportedException

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

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