C#学习笔记10:抽象类、抽象方法、接口、密封类、密封方法

-呈阶梯状分布- 22 次阅读 1349 字 发布于 2026-08-12 最后更新于 2026-08-12 C#学习笔记


抽象类/抽象方法

当我们希望父类中只有方法的声明,而没有方法的定义(空有方法却没有方法的代码块),将方法的定义放到继承了方法的子类中写时,就可以使用抽象方法。
拥有抽象方法的类必须为抽象类,所以使用抽象方法前要先将类标记为抽象类,也就是abstract class。我们以前一篇的书籍为例,将PrintInfo()改为抽象类,只在父类中声明,在子类中重写时定义:

using System;

namespace Csharp
{
    abstract class Book  // 书籍类(父类)
    {
        protected string name = "书籍名称";
        protected int id = 31;  // 书籍编号
        public abstract void PrintInfo();
    }
    class Novel : Book  // 小说类(子类)
    {
        public override void PrintInfo()
        {
            Console.WriteLine("书籍:{0}(编号{1})", name, id);
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

可以看到,父类中声明了PrintInfo()却没代码块,且加上了abstract,子类中以override重写了父类的PrintInfo()方法。

值得一提,抽象类中可以包含抽象方法,也可以包含一般方法,你可以视抽象类为一般类的拓展,而非限制,这只是给予了类写下抽象方法的权利。

接口

接口的定义

接口用 interface 接口名 的方式定义,且接口名一般以大写的I开头,用于标识接口。

using System;

namespace Csharp
{
    interface IBook  // 书籍(接口)
    {
        void Read();
    }
    class Novel : IBook  // 小说类(继承自接口)
    {
        public void Read()
        {
            Console.WriteLine("小说被阅读");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

接口和抽象类的区别

接口类似于抽象类,但又有许多不同。接口中的成员方法只需要写返回值和方法名即可,不需要写作用域,这是因为作用域同一为public。
抽象类可以和正常类一样拥有成员变量、属性、构造方法,但是接口不行,接口只能有成员方法,以供继承重写。接口和抽象类都必须实现继承的抽象方法。

接口和类的继承

接口可以继承自一个或多个接口,类可以继承自一个或多个接口,类在继承一个或多个接口的同时可以再继承一个类,类不允许继承自多个类。继承时用逗号分开即可。

using System;

namespace Csharp
{
    interface IBook  // 书籍(接口)
    {
        void Read();
    }
    interface IPublication
    {
        int ReturnID();
    }
    class Book
    {
        protected string name = "";
        protected int id = 0;
    }
    class Novel : Book, IBook, IPublication  // 小说类(继承自接口)
    {
        public void Read()
        {
            Console.WriteLine("小说被阅读");
        }

        public int ReturnID()
        {
            return id;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

密封类/密封方法

密封方法

先看下面的代码:

using System;

namespace Csharp
{
    class _BaseClass
    {
        public virtual void Test()
        {
            Console.WriteLine("父类");
        }
    }
    class _DrivedClass1 : _BaseClass
    {
        public override void Test()
        {
            Console.WriteLine("子类1");
        }
    }
    class _DrivedClass2 : _DrivedClass1
    {
        public override void Test()
        {
            Console.WriteLine("子类2");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

我们做了一个继承链条:子类1继承自父类,而子类2继承自子类1。且在子类1中重写父类的Test()方法,又在子类2中重写子类1的Test()方法。
那么,此时,如果我希望子类1重写了父类Test()方法后,子类2不能继续重写Test()方法,那就可以用到密封方法。
输入下面代码:

using System;

namespace Csharp
{
    class _BaseClass
    {
        public virtual void Test()
        {
            Console.WriteLine("父类");
        }
    }
    class _DrivedClass1 : _BaseClass
    {
        public sealed override void Test()
        {
            Console.WriteLine("子类1");
        }
    }
    class _DrivedClass2 : _DrivedClass1
    {
        public override void Test()
        {
            Console.WriteLine("子类2");
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

可以看到,我们在子类1的Test()方法前加上了sealed,用于标记该方法已被密封,无法被子类的子类重写。
不出意外,此时的编译器就会报错了:

Image

可以看到,我们成功对子类1中的Test()方法密封,使其不能被新的子类重写。

密封类

我们在class前面写上sealed,使得类被标记为密封类,则该类不能被任何类继承,也不能拥有虚方法。

Image
Image

后话

本专栏中的专业术语有些混乱,在这里统一写一下。
之前一直将"在类中直接声明的变量"称为"成员变量",在之后则会改名为"字段"。
"声明"指的是为变量指定类型/为函数指定函数名、返回值、作用域等,不包括赋值/写入函数体。
"定义"指的是为变量赋值的过程/为函数写入函数体的过程。
"实现"指的是子类重写父类中的抽象方法/接口中的方法的过程。
"对象"指的是用类作为模板创建的数据,是类的实例。
"实例化"指的是用new关键字创建一个对象的过程。
"重写"指的是子类用自己的独有方法(与父类同名)覆盖父类方法(也可能是接口方法)。
"函数重载"指的是同名函数(或方法),通过参数不同执行不同的函数体。
"函数体"指的是函数(或方法)大括号内的内容。
"方法"指的是类(也可能为接口或结构体)里面的函数。
"变量"指的是值被存储时赋予的名称。
暂时只想到这些,后面有漏掉的解释再补上吧。
感谢阅读~下一篇讲索引器、运算符重载、ToString()方法!