虚方法、抽象类与抽象方法
抽象类
何时必须声明一个类为抽象类?(面试题)
当这个类中包含抽象方法时,或是该类并没有完全实现父类的抽象方法时。
abstract 修饰符可用于类、方法、属性、索引和事件。 在类声明中使用 abstract
修饰符以指示某个类仅旨在作为其他类的基类。 标记为 abstract 的成员,或包含在抽象类中的成员,都必须由派生自抽象类的类来实现。
抽象类的特征:
- 抽象类由abstract关键字修饰,只能用作基类
- 抽象类可能包含抽象方法和访问器,同时也可以包含非抽象方法和非抽象访问器(只要有一个抽象方法,该类就必须定义为抽象类)
1 abstract class AbstractClass 2 { 3 public abstract int Age { get; }//抽象访问器 4 public string Name { get { return "张三"; } }//非抽象访问器 5 6 public abstract void AbstractMethod();//抽象方法 7 8 public void Method()//非抽象方法 9 { 10 Console.WriteLine("抽象类中的非抽象方法"); 11 } 12 }
- 派生自抽象类的非抽象类,必须实现抽象类的所有抽象方法和访问器,否则该子类也必须声明为抽象类
1 class MyClass : AbstractClass 2 { 3 //实现抽象类中的抽象访问器 4 public override int Age 5 { 6 get 7 { 8 return 30; 9 } 10 } 11 //实现抽象类中的抽象方法 12 public override void AbstractMethod() 13 { 14 Console.WriteLine("在派生类中实现的抽象方法"); 15 } 16 }
- 抽象类不能被实例化(但是抽象类中可以有构造函数),只能通过派生类进行实例化
1 class Program 2 { 3 static void Main(string[] args) 4 { 5 //AbstractClass abstractClass = new AbstractClass();//抽象类不能实例化,这种写法会报错 6 MyClass myclass = new MyClass(); 7 Console.WriteLine(myclass.Age);//30 8 Console.WriteLine(myclass.Name);//张三 9 myclass.Method();//抽象类中的非抽象方法 10 myclass.AbstractMethod();//在派生类中实现的抽象方法 11 12 Console.ReadKey(); 13 } 14 }
- 抽象类不能使用sealed修饰符来修饰,因为abstract和sealed两个修饰符有相反的含义(sealed表示该类不能被继承,abstract表示该类可以被继承)
- 抽象类可以被抽象类继承,此时抽象的派僧类可以对抽象基类中的抽象方法进行重写,也可以不进行重写,但是必须在该抽象派生类的子类中对其进行重写
抽象方法和抽象访问器
- 抽象方法使用abstract修饰,使用static和virtual修饰是错误的
- 抽象方法是隐式的虚方法
- 只有抽象类才允许声明抽象方法
- 抽象方法只有方法签名,没有方法具体实现的方法体
- 在静态属性上是用abstract修饰符是错误的
- 通过override修饰符,可以在派生类中重写抽象类中的抽象方法和抽象访问器
- 抽象成员不能是私有的
虚方法
- 虚方法使用virtual关键字修饰
- 虚方法不能是私有的
- virtual修饰符不能与static、abstract、private、override修饰符一起使用
- 通过override关键字对虚方法进行重写
1 class Class1 2 { 3 public virtual void ShowInfo() 4 { 5 Console.WriteLine("虚方法在父类中的实现"); 6 } 7 } 8 9 class Class2 : Class1 10 { 11 public override void ShowInfo() 12 { 13 Console.WriteLine("虚方法在子类中的实现"); 14 } 15 } 16 17 class Program 18 { 19 static void Main(string[] args) 20 { 21 Class2 c = new Class2(); 22 c.ShowInfo();//虚方法在子类中的实现 23 24 Console.ReadKey(); 25 } 26 }
1、当调用一个对象的函数时,系统会先去检查这个对象所属的类,看所调用的函数是否为虚函数;
2、如果不是虚函数,那么它就直接执行该函数。而如果有virtual关键字,也就是一个虚函数,那么这个时候它就不会立刻执行该函数了,而是转去检查对象的实例类。
3、在这个实例类里,他会检查这个实例类的中是否重写该虚函数(通过override关键字),如果是有,那么就马上执行该实例类中的这个重新实现的函数;如果没有的话,系统就会不停地往上找实例类的基类,直到找到第一个重写了该虚函数的基类为止,然后执行该基类中的函数。
抽象方法与虚方法的区别:
- 抽象方法必须定义在一个抽象类中,虚方法没有特殊要求
- 抽象方法在基类中不可以有具体的实现,而虚方法在基类中必须有具体的实现
- 抽象方法要求必须在派生类中对其进行重写;而虚方法可以在派生类中对其进行重写,也可以不对其进行重写
抽象类与接口的相同点:
- 都可以被继承
- 都不可以实例化
- 都可以包含方法声明
- 派生类都必须实现其未实现的方法
抽象类与接口的区别:
- 抽象类可以定义字段、属性,可以包含方法的实现,可定义的内容与非抽象类基本一致;而接口只能定义属性、索引器、事件和方法声明,不能包含字段和方法实现
- 抽象类是一个不完整的类,需要进一步细化扩展;而接口是一个行为规范
- 抽象类更多的是定义在一系列紧密相关的类之间;而接口大多数是关系疏松但都实现某一功能的类中
- 抽象类是从一系列相关对象中抽象出来的概念, 因此反映的是事物的内部共性;接口是为了满足外部调用而定义的一个功能约定, 因此反映的是事物的外部特性
- 接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法
- 可以用于支持回调,而继承并不具备这个特征
- 抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的
- 如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法