【问题标题】:Abstract Method in Non Abstract Class非抽象类中的抽象方法
【发布时间】:2012-09-16 20:37:45
【问题描述】:

我想知道在非抽象类(在 C# 中)中限制抽象方法的设计背后的原因。

我知道类实例没有定义,因此它们不能被调用,但是当定义静态方法时,它们也被排除在实例之外。为什么不以这种方式处理抽象方法,有什么具体原因吗?

可以在具体类中允许它们,并且可以强制派生类实现方法,基本上就是在抽象类中的抽象方法的情况下所做的。

【问题讨论】:

  • 我希望我不是唯一一个说..“Wut?”
  • 您想在非抽象类中定义抽象方法的用例是什么?
  • 请添加一个这样的概念有用的类示例。
  • 取任何抽象类,比如一个抽象方法和多个具体方法。如果我没有限制,这个抽象类不能去掉还是达到同样的目的吗?
  • 如果您对此有信心,也许您可​​以考虑使用virtual 方法并让它抛出NotImplementedException(或什么都不做,或返回一个默认值)。然后记录继承的类必须实现该方法。但总的来说,我同意其他人的观点;这听起来像是您在尝试使用错误的工具来完成这项工作。

标签: c# oop compiler-construction


【解决方案1】:

首先,我认为您的要求在逻辑上没有意义。如果您有abstract 方法,则基本上意味着该方法未完成(正如@ChrisSinclair 指出的那样)。但这也意味着整个班级都没有完成,所以它也必须是abstract。

或者换一种说法:如果你在一个不是abstract 的类上有一个abstract 方法,那就意味着你有一个不能被调用的方法。但这意味着该方法没有用,您可以将其删除,并且都可以正常工作。

现在,我将尝试使用一个更具体的示例:想象以下代码:

Animal[] zoo = new Animal[] { new Monkey(), new Fish(), new Animal() };

foreach (Animal animal in zoo)
    animal.MakeSound();

这里,Animal 是非abstract 基类(这就是为什么我可以将它直接放入数组的原因),Monkey 和Fish 是从Animal 派生的,MakeSound() 是abstract 方法。这段代码应该做什么?你没有说清楚,但我可以想象几个选择:

  1. 您不能对类型为Animal 的变量调用MakeSound(),只能使用类型为派生类之一的变量调用它,因此这是一个编译错误。

    这不是一个好的解决方案,因为abstract 的全部意义在于能够将派生类的实例视为基类,并且仍然获得特定于派生类的行为。如果你想要这个,只需在每个派生类中添加一个普通的(没有abstract、virtual 或override)方法,不要对基类做任何事情。

  2. 您不能在运行时类型实际上是 Animal 的对象上调用 MakeSound(),因此这是运行时错误(异常)。

    这也不是一个好的解决方案。 C# 是一种静态类型的语言,因此它会尝试在编译时捕获诸如“您不能调用此方法”之类的错误(反射和dynamic 等明显的异常除外),因此将其变为运行时错误不适合语言的其余部分。此外,您可以通过在引发异常的基类中创建virtual 方法来轻松完成此操作。

总而言之,您想要一些没有多大意义的东西,并且带有糟糕设计的味道(一个行为与其派生类不同的基类)并且可以很容易地解决。这些都是应该不实施的功能的所有迹象。

【讨论】:

  • 对我有意义,第 1 点给了我答案,为什么在“类级别”有抽象以及为什么应该限制抽象类创建实例。谢谢维克。
【解决方案2】:

所以,你想允许

class C { abstract void M(); }

编译。假设确实如此。当有人这样做时,您希望发生什么

new C().M();

?您想要执行时错误吗?好吧,通常 C# 更喜欢编译时错误而不是执行时错误。如果您不喜欢这种理念,可以使用其他语言...

【讨论】:

  • 我陈述了这个问题并给出了编译器正在处理的静态方法的例子。我的问题是关于抽象方法的编译器设计,基本上为什么他们专门为这些方法选择抽象类?
  • @AshishJain 因为不能直接实例化abstract 类。它是“未完成的”,就像 interface 不能直接实例化一样,因为空方法/属性所做的是未定义的。它迫使希望使用它们的程序员在能够实例化之前继承和实现 all 抽象方法。编辑:静态方法是一个 不同的 概念。它们属于 Type 而不是对象实例。抽象方法在 instance 级别,需要实现(还要注意静态方法需要实现——不存在“抽象静态”)
  • 如果你想要执行时错误,你已经可以这样做了:创建一个抛出异常的虚拟方法。
  • 静态方法是另一种情况,因为它们在任何情况下都不在实例上。我不知道您所说的“为什么他们专门为这些方法选择抽象类?”。如果允许具体类具有抽象方法,我已经向您展示了潜在的问题。你宁愿发生什么?
  • @AakashM 如果抽象方法有返回类型而不是 void,那就更有趣了。 :) 至少一个 void 方法可以做nothing,但是返回类型有什么作用呢? default 值?是的,我不知道还会发生什么。摘要的存在是有原因的,其他任何事情都可以按照我们的建议由virtual 处理。
【解决方案3】:

我认为您已经回答了自己的问题,最初没有定义抽象方法。因此无法实例化该类。您是说它应该忽略它,但是根据定义,在添加抽象方法时,您是在说“由此创建的每个类都必须实现此{抽象方法}”,因此您定义抽象类的类也必须是抽象的,因为那时抽象方法仍未定义。

【讨论】:

    【解决方案4】:

    您可以使用“虚拟”方法实现您想要的,但使用虚拟方法可能会导致更多运行时业务逻辑错误,因为开发人员不会“强制”在子类中实现逻辑。

    我认为这里有一个有效的观点。抽象方法是完美的解决方案,因为它会“强制”在子程序中定义方法体的要求。

    我遇到过很多情况,父类必须(或者更有效地)实现一些逻辑,但“只有”子类可以实现其余逻辑”

    因此,如果有机会,我会很乐意将抽象方法与完整方法混合使用。

    @AakashM,我很欣赏 C# 更喜欢编译时错误。我也是。任何人也一样。这是关于开箱即用的思考。

    支持这一点不会影响这一点。

    让我们在这里跳出思维定势,而不是对大男孩的决定说“万岁”。

    C# 编译器可以直接检测并拒绝某人使用抽象类,因为它使用了“abstract”关键字。

    C# 也知道强制任何子类实现任何抽象方法。如何?因为使用了“抽象”关键字。

    这对于任何研究过编程语言内部的人来说都很容易理解。

    那么,为什么 C# 不能检测到普通类中方法旁边的“抽象”关键字并在编译时处理它。

    原因是需要“返工”,付出的努力不值得支持小需求。

    特别是在一个缺乏大男孩给他们的框框思考的人的行业。

    【讨论】:

      【解决方案5】:

      抽象类可能包含抽象成员。如果任何方法具有我们无法在同一个类中实现的抽象关键字,则只有方法声明。所以抽象类是不完整的。这就是为什么没有为抽象类创建对象的原因。

      非抽象类不能包含抽象成员。

      示例:

      namespace InterviewPreparation
      {
         public abstract class baseclass
          {
              public abstract void method1(); //abstract method
              public abstract void method2(); //abstract method
              public void method3() { }  //Non- abstract method----->It is necessary to implement here.
          }
          class childclass : baseclass
          {
              public override void method1() { }
              public override void method2() { }
          }
          public class Program    //Non Abstract Class
          {
              public static void Main()
              {
                  baseclass b = new childclass(); //create instance
                  b.method1();
                  b.method2();
                  b.method3();
              }
          }
      
      }
      

      【讨论】:

        【解决方案6】:

        目前还不清楚您为什么要这样做,但另一种方法可能是强制派生类提供委托实例。像这样的

        class MyConcreteClass
        {
          readonly Func<int, DateTime, string> methodImpl;
        
          // constructor requires a delegate instance
          public MyConcreteClass(Func<int, DateTime, string> methodImpl)
          {
            if (methodImpl == null)
              throw new ArgumentNullException();
        
            this.methodImpl = methodImpl;
          }
        
          ...
        }
        

        (当然,签名string MethodImpl(int, DateTime)只是一个例子。)

        否则,我可以推荐其他答案来解释为什么您的愿望可能不会让世界变得更美好。

        【讨论】:

        • 可以接受替代方案不是最好的,甚至有严重的缺点。但是为什么抽象类的当前实现(为什么不保留抽象方法/属性/索引器等,如虚拟)是最好的,并且“抽象类”的概念是不可避免的(由于它优于其他方式),请记住接口已经那里?
        【解决方案7】:

        所以上面的答案是正确的:拥有抽象方法使类本质上是抽象的。如果你不能实例化一个类的一部分,那么你就不能实例化这个类本身。但是,上面的答案并没有真正讨论您的选择。

        首先,这主要是 public 静态方法的问题。如果这些方法不打算公开,那么您可以保护非抽象方法,这些方法在抽象类声明中是允许的。因此,您可以将这些静态方法移到单独的静态类中,而不会出现太大问题。

        作为替代方案,您可以将这些方法保留在类中,但随后不使用抽象方法,而是声明一个接口。本质上,您有一个多重继承问题,因为您希望派生类从两个概念上不同的对象继承:一个具有公共静态成员的非抽象父类,以及一个具有抽象方法的抽象父类。与其他一些框架不同,C# 确实允许多重继承。相反,C# 提供了一个正式的接口声明来满足这个目的。此外,抽象方法的全部意义实际上只是强加了某种概念接口。

        【讨论】:

          【解决方案8】:

          我有一个非常类似于 OP 试图实现的场景。在我的情况下,我想要抽象的方法将是一个 protected 方法,并且只有基类知道。所以“新的 C().M();”不适用,因为所讨论的方法不是公开的。我希望能够在基类上实例化和调用公共方法(因此它必须是非抽象的),但是我需要这些公共方法来调用子类中受保护方法的受保护实现并且没有默认实现在父母。从某种意义上说,我需要强制后代覆盖该方法。由于依赖注入,我不知道编译时子类是什么。

          我的解决方案是遵循规则并使用具体的基类和虚拟的 protected 方法。但是,对于默认实现,我抛出 NotImplementedException 错误“必须在子类的实现中提供 方法名 的实现”。

          protected virtual void MyProtectedMethod() 
          { 
            throw new NotImplementedException("The implementation for MyProtectedMethod must be provided in the implementation of the child class."); 
          }
          

          这样一来,就永远无法使用默认实现,并且后代实现的实现者很快就会发现他们错过了重要的一步。

          【讨论】:

          • 所以你用运行时异常替换编译器错误?如果您的类不是抽象的,那么您可能会在某个时刻冒着在您的类的实例上调用您未实现的方法并引发异常的风险。如果您的意思是抽象,那么只需使用抽象,不要尝试解决它。​​
          • 运行时错误是一个合理的权衡,因为框架/语言没有提供其他机制来实现 OP 的要求。就我而言,我有信心如果遇到运行时错误,我会在开发过程的早期发现它。子类要么从未使用过,要么一直在使用(核心实用程序)。我只是提供了一种替代解决方案,以满足在需要在任何子类中覆盖的具体类中定义方法的需求。
          • Ed 我不清楚我的帖子如何通过提供框架/语言限制的解决方法来直接解决 OP 中的问题???如果您觉得我的帖子不合适,请直接与我联系。
          • 这里的情况相同,例如,如果方法是抽象的,为什么他们必须这样做,以便类必须是抽象的。该类显然不是未完成的,因为未完成的方法是预期的,因此该类已完成,具有未完成的方法。如果这有任何意义??
          猜你喜欢
          • 1970-01-01
          • 2012-09-21
          • 1970-01-01
          • 1970-01-01
          • 2016-12-05
          • 2017-03-26
          • 2021-10-13
          • 2016-05-10
          • 2016-06-20
          相关资源
          最近更新 更多