【问题标题】:Interface, Abstract, or just virtual methods?接口,抽象,还是只是虚拟方法?
【发布时间】:2011-09-28 20:24:30
【问题描述】:

我有一堆系统,我们称它们为A, B, C, D, E, F, G, H, I, J

它们都有相似的方法和属性。有些包含完全相同的方法和属性,有些可能略有不同,有些可能有很大差异。现在,每个系统都有很多重复的代码。例如,我有一个为每个系统定义的名为GetPropertyInformation() 的方法。我试图找出哪种方法是减少重复代码的最佳方法,或者以下方法之一可能不是可行的方法:

界面

public Interface ISystem
{
    public void GetPropertyInformation();
    //Other methods to implement
}

public class A : ISystem
{
    public void GetPropertyInformation()
    {
       //Code here
    }
}

摘要

public abstract class System
{
    public virtual void GetPropertyInformation()
    {
        //Standard Code here
    }
}

public class B : System
{
   public override void GetPropertyInformation()
   {
      //B specific code here
    }
}

超级基类中的虚拟方法

public class System
{
   public virtual void GetPropertyInformation()
    {
     //System Code
    }
}

public class C : System
{
  public override void GetPropertyInformation()
  {
      //C Code
  }
}

一个问题,虽然它可能很愚蠢,但让我们假设我采用抽象方法并且我想覆盖GetPropertyInformation,但我需要传递一个额外的参数,这是可能的还是我必须创建抽象类中的另一个方法?例如GetPropertyInformation(x)

【问题讨论】:

    标签: c# oop interface abstract-class virtual-functions


    【解决方案1】:

    您的抽象和“超级基类”方法并没有太大的不同。您应该始终使基类抽象,并且您可以提供默认实现(虚拟方法)或不提供(抽象方法)。决定因素是你是否想要拥有基类的实例,我认为不是。

    所以它介于基类和接口之间。如果您的 A、B C 类之间存在强耦合,那么您可以使用基类并且可能使用通用实现。

    如果 A、B、C 类自然不属于一个“家族”,则使用接口。

    System 并不是一个好名字。

    并且在覆盖时不能更改参数列表。也许默认参数会有所帮助,否则您只需要 GetPropertyInformation() 的 2 个重载。

    【讨论】:

    • 我感觉抽象基类和超级基类很相似。 A、B、C 系统确实具有很强的耦合性。有一个行业标准,但不一定要遵循,因此每个人都可以采用该标准,也可以根据自己的需要更改该标准。匆忙中,我选择了 System :)
    【解决方案2】:

    通常,当您想要共享实现并减少重复操作时,您会选择对象继承。否则接口会胜出,因为它们更灵活,因为不需要公共基类。

    至于重写方法和修改参数列表是不可能的。想象一下如何在基类或接口引用上调用该方法?

    【讨论】:

    • 这是“可能的”——你应该能够声明这样的方法,它不再是对原始方法的覆盖。
    • @Sii 这当然是不可能的。该问题要求覆盖该方法并添加一个参数。那是做不到的。
    • “当你想共享实现时选择继承”可能很麻烦。我会说:什么时候可以分享实施。类型关系首先应该是可靠的。
    【解决方案3】:

    我会选择下面添加的内容。您仍然可以从接口契约和共享实现中获益。

    public Interface ISystem
    {
        public void GetPropertyInformation();
        //Other methods to implement
    }
    
    public abstract class System : ISystem
    {
        public virtual void GetPropertyInformation()
        {
            //Standard Code here
        }
    }
    
    public class B : System
    {  
       public string ExtendedSystemProp {get;set;}
    
       public override void GetPropertyInformation()
       {
          base.GetPropertyInformation();
    
          var prop = "some extra calculating";
    
          GetExtraPropertyInformation(prop);
        }
    
        public void GetExtraPropertyInformation(string prop)
        {
             ExtendedSystemProp = prop;
        }
    }
    
    ISystem genericSystem = new B();
    genericSystem.GetPropertyInformation();
    
    (genericSystem as B).ExtendedSystemProp = "value";
    

    【讨论】:

    • 您能否详细说明您为什么要采用这种方法?
    • 这样,即使您只使用ISystem 接口进行编程(当您拥有它们时应该这样做),您仍然可以获得所有属性。但是,如果您在仅存在 B 的上下文中进行编码:B bSystem = new B(); 您还会公开 B 特定方法以供显式使用。
    【解决方案4】:

    您不能在覆盖中传递额外的参数。当您覆盖时,您将使用确切的签名覆盖该方法。我建议您传入一个接口参数,例如 IPropertyInformation,它可以根据实现进行更改。

    为您的实现选择基类还是接口的决定实际上取决于您的使用。 A-I 彼此之间是否有足够的共同点,以至于它们真的都应该从同一个基类派生而来?如果是这样,则使用基类。真的只有GetPropertyInformation 是共享的,否则系统在功能上完全不同?那么你真的只是希望他们共享一个界面。

    【讨论】:

      【解决方案5】:

      其他人已经涵盖了我的答案中最初的内容,但关于“添加参数”这一点:不要忘记最新的 C# 还允许您在方法中使用可选参数。

      【讨论】:

      • 但是我必须在一开始就声明可选参数。我可能直到 1 年后才知道我需要一个参数,所以在这种情况下,可选参数对我没有好处。
      【解决方案6】:

      除非你有令人信服的理由,否则我会选择界面。公共虚拟方法虽然做了很多,但还是not ideal

      【讨论】:

        猜你喜欢
        • 2011-10-25
        • 1970-01-01
        • 2011-06-13
        • 1970-01-01
        • 2018-05-23
        • 2021-12-17
        • 2016-07-08
        • 1970-01-01
        • 2011-07-18
        相关资源
        最近更新 更多