为什么C#允许抽象类没有抽象成员?

为什么C#允许抽象类没有抽象成员?,c#,language-design,abstract,C#,Language Design,Abstract,C#规范规定: 允许使用抽象类(但是 不需要)包含摘要 成员们 这允许我创建如下类: public abstract class A { public void Main() { // it's full of logic! } } 或者更好: public abstract class A { public virtual void Main() { } } public abstract class B : A { public

C#规范规定:

允许使用抽象类(但是 不需要)包含摘要 成员们

这允许我创建如下类:

public abstract class A
{
    public void Main() 
    {
        // it's full of logic!
    }
}
或者更好:

public abstract class A
{
    public virtual void Main() { }
}

public abstract class B : A
{
    public override sealed void Main()
    {
        // it's full of logic!
    }
}

这确实是一个具体的类;它只是抽象的,因为我们不能实例化它。例如,如果我想执行
B.Main()
中的逻辑,我必须首先获得B的一个实例,这是不可能的

如果继承者实际上不必提供实现,那么为什么称它为抽象的呢

换句话说,为什么C#只允许包含具体成员的抽象类


我应该提到,我已经熟悉抽象类型和成员的预期功能

这个想法是强制实现者从类派生,因为它只为可能更专业的实现提供基础。因此,基类虽然没有任何抽象成员,但可能只包含可作为扩展基础的核心方法和属性

例如:

public abstract class FourLeggedAnimal
{

    public void Walk()
    {
        // most 4 legged animals walk the same (silly example, but it works)
    }

    public void Chew()
    {

    }
}

public class Dog : FourLeggedAnimal
{
    public void Bark()
    {
    }
}

public class Cat : FourLeggedAnimal
{
    public void Purr()
    {
    }
}
public abstract class Pet
{
    public string Name{get;set;}
}

public class Dog : Pet
{
    public void Bark(){ ... }
}

编译器不会阻止实现逻辑,但在您的情况下,我只会省略抽象?!顺便说一句,有些方法可以通过
{throw Exception(“must inherit”);}实现
,编译器无法区分完全实现的类和函数,只包括throw

我认为更准确地表达您的问题是:为什么C允许只包含具体成员的抽象类


答案是:没有理由不这样做。也许有人有某种组织结构,他们喜欢在顶部有一个不可实例化的类,即使它下面的类只是继承而不添加任何内容。没有理由不支持这一点。

一个很好的例子可能是一个公共基类,它为派生类提供共享属性和其他成员,但不表示具体对象。例如:

public abstract class FourLeggedAnimal
{

    public void Walk()
    {
        // most 4 legged animals walk the same (silly example, but it works)
    }

    public void Chew()
    {

    }
}

public class Dog : FourLeggedAnimal
{
    public void Bark()
    {
    }
}

public class Cat : FourLeggedAnimal
{
    public void Purr()
    {
    }
}
public abstract class Pet
{
    public string Name{get;set;}
}

public class Dog : Pet
{
    public void Bark(){ ... }
}
所有宠物都有名字,但宠物本身是一个抽象的概念。宠物的例子必须是狗或其他种类的动物

这里的区别在于,基类没有提供应该由实现者重写的方法,而是声明所有PET至少由一个
名称
属性组成;它只是一个模板

如果您将它声明为抽象类,那么它就不是“真正的具体类”。可作为设计选项提供给您


这种设计选择可能与创建实体有关,这些实体(有可能混淆术语)是真实世界对象的抽象,并且具有可读性。您可能希望声明
Car
类型的参数,但不希望对象被声明为
Car
——您希望
Car
类型的每个对象都被实例化为
卡车
轿车
轿跑车
,或
跑车
Car
不要求继承人添加实现,这一事实并不会降低其作为继承人抽象版本的价值,因为继承人本身无法实例化。

抽象意味着提供行为的抽象。例如,车辆是一种抽象形式。它没有任何真实的例子,但我们可以说,车辆有加速行为。更具体地说,福特Ikon是一款汽车,雅马哈FZ是一款汽车。这两者都有加速行为


如果你现在在课堂上做这个。车辆是一个抽象类,采用加速度法。而您可能/可能不提供任何抽象方法。但业务需求是不应该实例化车辆。因此你把它抽象化了。其他两个类-Ikon和FZ是从车辆类派生的具体类。这两者将有各自的属性和行为。

这里有一个潜在的原因:

这对所有的物体来说都很常见 在一个层中有你没有的方法 想在整个过程中复制吗 系统。你可以移动所有这些 将行为转换到公共层 超级类型

--马丁·福勒


没有理由阻止在抽象类中只使用具体方法——这只是不太常见而已。层超类型是一种可能有意义的情况。

我看到抽象类有两个主要用途:

  • 一个不完整的类,必须专门化才能提供一些具体的服务。在这里,抽象成员是可选的。该类将提供一些子类可以使用的服务,并可以定义它用来提供其服务的抽象成员,如。这种类型的抽象类旨在创建继承层次结构

  • 仅提供静态实用程序方法的类。在这种情况下,抽象成员毫无意义。C#支持这个概念,因为它们是隐式抽象和密封的。这也可以通过使用私有构造函数的密封类来实现


关于用法,在类声明上使用
抽象
,但没有抽象成员,与在类的构造函数上使用
公共
,但使用
保护
是一样的。这两种方法都强制派生类,以便对其进行实例化


然而,就自文档代码而言,通过标记类
abstract
,它会通知其他人,该类永远不会自行实例化,即使它没有
virtual
abstract
成员。然而,保护构造函数不会做出这样的断言。

一个突出的例子是System.Windows.Forms.ButtonBase,Button、CheckBox和RadioButton就是从中派生出来的。它没有抽象成员,但有很多虚拟方法和属性,这就引出了一个问题。如果,正如您所说,“我们的想法是强迫实现者按照我所说的那样离开类