抽象类/抽象方法
当我们希望父类中只有方法的声明,而没有方法的定义(空有方法却没有方法的代码块),将方法的定义放到继承了方法的子类中写时,就可以使用抽象方法。
拥有抽象方法的类必须为抽象类,所以使用抽象方法前要先将类标记为抽象类,也就是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,用于标记该方法已被密封,无法被子类的子类重写。
不出意外,此时的编译器就会报错了:

可以看到,我们成功对子类1中的Test()方法密封,使其不能被新的子类重写。
密封类
我们在class前面写上sealed,使得类被标记为密封类,则该类不能被任何类继承,也不能拥有虚方法。


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

Comments NOTHING