我想了解在C#中为什么要设计限制在非抽象类中使用抽象方法。
我知道如果实例化一个类,它没有定义抽象方法,因此无法调用,但是当定义静态方法时,它们也被从实例中排除。为什么不以同样的方式处理抽象方法,在具体类中允许使用并强制派生类来实现这些方法?
在具体类中,抽象方法可以被允许,并且可以强制派生类来实现这些方法,这基本上就是在抽象类中使用抽象方法的做法。
我想了解在C#中为什么要设计限制在非抽象类中使用抽象方法。
我知道如果实例化一个类,它没有定义抽象方法,因此无法调用,但是当定义静态方法时,它们也被从实例中排除。为什么不以同样的方式处理抽象方法,在具体类中允许使用并强制派生类来实现这些方法?
在具体类中,抽象方法可以被允许,并且可以强制派生类来实现这些方法,这基本上就是在抽象类中使用抽象方法的做法。
首先,我认为你提出的问题在逻辑上是没有意义的。如果你有一个抽象
方法,这基本上意味着这个方法是未完成的(就像@ChrisSinclair指出的那样)。但这也意味着整个类是未完成的,所以它也必须是abstract
。
或者换一种说法:如果你在一个不是abstract
的类中有一个abstract
方法,那么这意味着你有一个无法被调用的方法。但这意味着这个方法是没有用的,你可以将其删除,程序运行结果不会变化。
现在,我将通过使用示例来更加具体地阐述:
Animal[] zoo = new Animal[] { new Monkey(), new Fish(), new Animal() };
foreach (Animal animal in zoo)
animal.MakeSound();
在这里,Animal
是非抽象基类(这就是为什么我可以将其直接放入数组中),Monkey
和Fish
是从Animal
派生出来的,而MakeSound()
是抽象方法。这段代码应该做什么?你没有清楚地说明,但我可以想象几个选项:
你不能在一个类型为Animal
的变量上调用MakeSound()
,只能使用一个以派生类命名的变量来调用它,因此会导致编译错误。
这不是一个好的解决方案,因为abstract
的整个意义在于能够将派生类的实例视为基类,并且仍然获得特定于派生类的行为。如果你需要这样做,只需在每个派生类中放置一个正常的(无abstract
、virtual
或override
)方法,并且不对基类进行任何操作。
你不能在运行时类型实际上为Animal
的对象上调用MakeSound()
,因此这会导致运行时错误(异常)。
这也不是一个好的解决方案。C#是一种静态类型的语言,因此它试图在编译时捕获像“你不能调用这个方法”这样的错误(显然有例外情况,如反射和dynamic
),所以将其变成运行时错误并不符合语言的其他部分。此外,你可以通过在基类中创建一个抛出异常的virtual
方法来轻松地实现此操作。
总之,你想要一些没有多大意义、设计不良的东西(一个基类的行为与其派生类不同),而且可以很容易地解决。这些都是不应该实现的特性的迹象。
所以,您想允许
class C { abstract void M(); }
编译代码。假设它成功了。接下来,当有人执行该代码时,你希望发生什么?
new C().M();
? 您想要一个运行时错误吗?总的来说,C# 更喜欢编译时错误而不是运行时错误。如果您不喜欢这种哲学,还有其他语言可供选择...
抽象类
不能直接实例化。它是“未完成的”,就像接口
不能直接实例化一样,因为空方法/属性的作用未定义。它强制希望使用它们的程序员在能够实例化之前继承和实现所有抽象方法。编辑:静态方法是一个_不同的_概念。它们属于_Type_而不是对象实例。抽象方法在_instance_级别上,并且需要被实现(还要注意静态方法需要被实现——不存在“抽象静态”)。 - Chris Sinclair我认为你已经解答了自己的问题,抽象方法起初是未定义的,因此该类无法实例化。你说它应该忽略它,但根据定义,当添加一个抽象方法时,你正在说“从这个创建的每个类必须实现这个{抽象方法}”,因此在定义抽象类的类中,抽象方法在那一点上仍然未定义,因此该类也必须是抽象的。
抽象类可能包含抽象成员。如果任何方法具有abstract关键字,则只有方法声明,我们不能在同一类中实现。因此,抽象类是不完整的。这就是为什么不能为抽象类创建对象的原因。
非抽象类不能包含抽象成员。
例子:
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();
}
}
}
目前仍不清楚您为什么需要这样做,但另一种方法是强制派生类提供委托实例。可以像这样:
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)
只是一个例子。)
否则,我可以推荐其他答案来解释为什么您的愿望可能不会让世界变得更好。
首先,这主要是公共静态方法的问题。如果这些方法不打算公开,则可以有受保护的非抽象方法,在抽象类声明中允许这些方法。因此,您可以将这些静态方法移动到单独的静态类中,而不会有太多问题。
作为替代方案,您可以将这些方法保留在类中,但是除了具有抽象方法之外,还可以声明一个接口。实际上,您有一个多重继承问题,因为您希望派生类从两个概念上不同的对象继承:具有公共静态成员的非抽象父级和具有抽象方法的抽象父级。与其他一些框架不同,C#允许多重继承。相反,C#提供了一个正式的接口声明,旨在填补此目的。此外,抽象方法的整个重点实际上只是强制执行某种概念接口。
我有一个与OP试图实现的情况非常相似的场景。在我的情况下,我想要抽象化的方法将是一个protected方法,并且只能被基类知道。因此,“new 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.");
}
通过这种方式,永远不会使用默认实现,并且后代实现的实现者将很快看到他们错过了重要的步骤。