抽象类抽象方法
抽象类不能实例化,而抽象方法不能直接实现,必须在非抽象的派生类中重写。
抽象方法本身也是虚拟的,可以使用 override 在派生类中重写。尽管重写方法不需要提供 virtual关键字。
如果类包含抽象方法,则该类也是抽象的,也必须声明为抽象的。
抽象类,抽象方法使用关键字 abstract
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static System.Console;
namespace ConsoleApp9
{
public abstract class MyClass//抽象类
{
public abstract void F();//抽象方法
}
public class MyClass2 : MyClass//创新类派生
{
public override void F()//抽象方法重写
//必须实现抽象基类的所有抽象成员
{
WriteLine("抽象方法重写");
}
}
class Program
{
static void Main(string[] args)
{
MyClass x = new MyClass2();//可以声明抽象类变量,但是不能实例化,可以实例化派生类
x.F();
ReadKey();
}
}
}
抽象类不能实例化,但是可以声明抽象类变量,实例化派生类。
抽象基类的派生类需要实现抽象基类的所有抽象成员。
密封类和密封方法
如果不应创建某个自定义类的派生类,该自定义就应该密封。
使类不可以创建派生类,可以使用私有构造函数。
还可以给类添加 sealed 修饰符。
给方法添加 sealed 修饰符表示不能重写该方法。
sealed class MyClass
{
}
在类或方法使用 sealed 修饰符时,最可能的情况是在类,库或自己编写的其他类的操作中,类或方法是内部的,任何尝试重写它的一些功能都可能导致代码的不稳定。
对于密闭类,编译器知道不能派生类,因此用于虚拟方法的虚拟表可以缩短或删除,以提高性能。
密闭方法,将方法声明为密闭会使其不能重写。
class MyClass1
{
public virtual void F()
{
WriteLine("虚方法");
}
}
class MyClass2 : MyClass1
{
public sealed override void F()//在重写基类方法时签名(所有参数类型和方法名)和返回类型必须完全匹配
{
WriteLine("重写方法");
}
}
在对密闭方法声明后,不允许其他方法再对该虚方法重写。
派生类的构造函数
假设没有为任何类构造显示的构造函数,编译器会为所有类提供默认的初始化构造函数。
但在添加一个构造函数后,就要通过派生类的层次结构高效的控制构造过程。
为了确保构造过程的顺利,不要出现不能按照层次结构进行构造的情况。
为什么会这样?因为在创建派生类实例时,实际上会有多个构造函数起作用。
要实例化的类本身不能初始化类,还必须调用基类中的构造函数。
接口
接口定义了所有类继承接口时应遵循的语法合同。接口定义了语法合同 "是什么" 部分,派生类定义了语法合同 "怎么做" 部分
如果有类派生自一个接口,声明这个类就会实现某些函数。
接口不能有任何实现代码,它是纯粹抽象的,因为接口成员总是抽象的,所以接口不需要 abstract 关键字。
类似于抽象类,永远不能实例化接口,只能包含其他成员的签名。
接口不能有构造函数,也不能有字段,不允许运算符重载。
接口成员不允许声明成员修饰符,接口成员总是隐式public。
接口定义了属性、方法和事件,这些都是接口的成员。接口只包含了成员的声明。成员的定义是派生类的责任。接口提供了派生类应遵循的标准结构。
定义接口
接口使用 interface 关键字声明,它与类的声明类似。接口声明默认是 public 的。
using System;
interface Myinterface
{
// 接口成员
void MyMethod();//声明一个方法
//没有具体的实现
}
class MyClass : Myinterface//类继承接口
{
public void MyMethod()//实现方法
{
Console.WriteLine("这是接口中的方法");
Console.ReadKey();
}
static void Main()
{
MyClass iImp = new MyClass();//创建实例对象
iImp.MyMethod();//实例调用接口方法
}
}
实现接口
方法名必须与接口定义的方法名一致。
接口的实现与类的继承语法格式类似:
class 类名 : 接口名
{
}
接口仅表示其成员的存在性,类负责确定这些成员是虚拟的还是抽象的。
接口类可以看成类引用,它可以引用任何实现该接口的类。
派生的接口
接口可以彼此继承。
如果一个接口继承其他接口,那么实现类或结构就需要实现所有接口的成员。
interface IParentInterface
{
void ParentInterfaceMethod();
}
interface IMyInterface : IParentInterface//接口继承
{
void MethodToImplement();
}
class InterfaceImplementer : IMyInterface//类继承派生接口
{
static void Main()
{
InterfaceImplementer iImp = new InterfaceImplementer();//创建实例对象
iImp.MethodToImplement();
iImp.ParentInterfaceMethod();
Console.ReadKey();
}
public void MethodToImplement()
{
Console.WriteLine("派生接口方法");
}
public void ParentInterfaceMethod()
{
Console.WriteLine("父类接口方法");
}
}
is和as
可以把具体类型的对象直接分配给基类或者接口,如果这些类型在层次结构中有直接关系的话。
as 强制转换,即使转换失败也不会抛出异常。
is 判断对象是否是同一类型。
is就是处于对类型的判断。返回true和false。如果一个对象是某个类型或是其父类型的话就返回为true,否则的话就会返回为false。另外is操作符永远不会抛出异常。
System.Boolean b1 = (o is System.Object);//b1 为true
System.Boolean b2 = (o is Employee);//b2为false
如果对象引用为null,那么is操作符总是返回为false,因为没有对象可以检查其类型.
if(o is Employee) {
Employee e = (Employee) o;
//在if语句中使用e
}
在上面代码中,其实clr对对象类型检查了两次:is操作首先检查o所引用的对象是否和employee类型兼容。如果兼容,那么在if语句内clr在执行转换时又会检查o是否为一个Employee引用。这种编程范式十分常见,c#便提供了一种新的类型检查,转换方式。即as操作符,他可以在简化代码的同时,提高性能。
Employee e = o as Employee;
if(e != null)
{
//在if语句中使用e
}
这种as操作即便等同于上面代码,同时只进行了1次的类型检查,所以提高了性能。如果类型相同就返回一个非空的引用,否则就返回一个空引用。