没有抽象方法的抽象类

15

我很惊讶地知道在C#中,即使没有抽象方法,也可以创建抽象类。

abstract class AbstractDemo
{
 public void show()
  {
    Console.WriteLine("In Show Method"); 
   }
}
class MainDemo:AbstractDemo
{
 public static void Main()
 {
    Console.WriteLine("In Main Method");
 }
}

有什么解释吗?

8个回答

33
有时您可能不想给出实例化类的可能性,但您需要将此类作为其他类的基类。
选择抽象类而不是接口的原因是您可以提供一些基本实现。

好的,这意味着我们可以将这样一个抽象类只作为其他类的标记类使用。 - Mohammad Nadeem
确实是这样 - 但如果您不需要实现,可以使用接口。 - Itay Karo
但是你可以使用空主体派生,那么强制用户这样做有什么意义呢?此外,C# 8允许在接口类中进行默认实现。因此,我认为您应该在需要一些实例字段时使用抽象类。 - pooya13
@pooya13 不确定我理解你关于使用空主体派生的观点。关于默认实现,你是正确的。这个答案是在 C# 8 很久以前写的。 - Itay Karo
没关系。可以理解。谢谢。 - pooya13

15

这是完全有效的,有时候还很有用,如果您想提供类似事件的行为:提供一个抽象类,其中所有的“事件处理程序”都实现为虚方法,并且默认行为是什么都不做。

我个人也经常将其用于抽象远程 API 客户端类,其中所有方法都会抛出异常:它们是为了测试替身而抽象化的,期望我们的实现是唯一的生产实现,但允许用户手动创建自己的测试替身或通过模拟框架创建。将方法设置为虚方法而不是抽象方法意味着可以添加新的 RPC 而不会造成破坏性变化。

然后,派生类可以重写一些方法,但不必覆盖任何具体的方法,因为没有任何抽象内容。对于该类仍然是抽象的,因为基类的实例将是无意义的(因为一切都是空操作)。

这种模式在 Java 中比在 C# 中更常见,因为在 C# 中,您通常只需要使用“真正的”事件即可。


这里唯一没有循环论证的答案(即说它是可能的,因为它是可能的,而不是解释为什么)我只会说,如果情况是这样的,在C#中这是相当无用的,清楚地说明这一点会很好,如果不是,给出一个示例用例。 - pooya13
@pooya13:我现在添加了一个示例(恰好,11年前我无法提供这个示例...) - Jon Skeet
谢谢。顺便说一句,看到你的分数后我有些不敢相信,我不确定我是否看对了“m”! - pooya13

5

抽象类是必须在使用之前扩展的类。这并不意味着函数本身必须是抽象的。

以动物类为例。

public abstract class Animal
{
    void Move()
    {
        //whatever
    }
}

public class Fish : Animal
{
    void Swim()
    {

    }
}

public class Dog : Animal
{
    void Bark()
    {

    }
}

所有动物都能移动,但只有鱼能游泳,狗能吠叫。
或者以一个现实生活的例子来说,我有一个在我的应用程序中使用的Asp.net MVC基控制器。它有一些基本方法,我非常经常需要像GetCurrentUser()和我编写的帮助本地化的函数。它还负责跟踪,所以我不必在所有的控制器中重新编写代码。这个类大约有200行代码,但没有一个抽象方法。

10
实际上,我认为你会发现大多数狗都会游泳 ;) - Paul Ruane

3

我认为你把抽象类和接口混淆了。接口不能有具体的方法实现,而抽象类可以。有时候你想防止用户实例化一个特定类的对象,但仍然要为派生自它的类提供一些基本功能;这就是抽象类的用处。


一个完全没有方法的抽象类怎么样?甚至包括定义都没有。 - Mohammad Nadeem
在这种情况下,它几乎与“标记”接口相同(即没有任何东西,只是标记一个类;请注意,FxCop建议使用属性代替)。区别在于您只能拥有一个基类(例如您的“标记抽象类”),但可以实现许多接口。因此,在这种情况下,接口通常更好。 - Daniel Rose
是的,但是假设我们想要一些类的属性,如果我们使用接口,那么默认情况下它们将是公共的,这将违反面向对象编程的基本数据隐藏原则。 - Mohammad Nadeem
我认为你的回答已经过时了,因为自从C#8以后,接口是可以有默认实现的。 - pooya13

2
如果你的类只是其他类的基础,而且它本身没有完整的可用性——换句话说,作为一个基础本身根本不可用,那么你就想防止创建它的实例。在这种情况下,你可以创建一个没有抽象成员的抽象类。

0
我们听说,在抽象类中必须有一个抽象成员。但是当我编译没有抽象方法的抽象类时,它可以编译通过。这让我惊讶。现在我找不到解释抽象类确切行为的文章了。

0

你可以在一个类上使用抽象关键字,仅仅是为了告诉编译器它只能被继承使用,而不能直接使用;在这种情况下,你不必在类上放置抽象成员。

这相当于在类中只放置一个受保护的构造函数,但使用抽象更清晰易懂。


0

没有比MSDN本身更好的解释了 http://msdn.microsoft.com/en-us/library/aa645615(v=VS.71).aspx

  • 抽象类不能直接实例化,使用new运算符在抽象类上是编译时错误。虽然可能有变量和值的编译时类型是抽象的,但这些变量和值必定要么为null,要么包含对派生自抽象类型的非抽象类实例的引用。
  • 允许(但不是必须)在抽象类中包含抽象成员。
  • 抽象类不能被密封。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接