目录
抽象类(abstract)
1.基本定义:
抽象类关键字:abstract,是一个被继承的类;
abstract 修饰符可用于类、方法、属性、索引和事件。 在类声明中使用
abstract
修饰符来指示某个类仅用作其他类的基类,而不用于自行进行实例化。1.抽象类不能实例化,除此以外,具有类的其他特性;
2.抽象类中可以有抽象方法,抽象方法只能在抽象类中,抽象方法中没有方法体;
3.在抽象类中可以定义抽象方法,也可以定义非抽象方法。
4.通常抽象类会被其他类继承,并必须重写其中的 抽象方法 或者 虚方法。注意:
(1).抽象类中如果有抽象方法,那么被继承的具体类一定要实现抽象类中得所有抽象方法。
(2).当然,如果是抽象类B继承抽象类A,那么抽象类B可以实现基类A中的抽象方法,也可以不实现A中的抽象方法。(3).abstract 不能修饰字段;
(4)不能在初始化的时候赋值;
(5)抽象类中不建议使用实例属性和方法;
//public abstract int x_;报错 public abstract int x_ { get; set; }
2.抽象方法
用abstract修饰的方法,称为抽象方法。
访问修饰符 abstract 方法返回值类型 方法名(参数列表);
例如:
public abstract void Practice();
抽象方法是一种特殊的虚方法,它只起声明作用,所以只加一个“;”号,一定不能带实现部分。
需要注意的是,抽象方法必须定义在抽象类中。
3.抽象类中方法的调用
抽象类是不能实例化的,但是可以让: 抽象类的实例化=子类的对象;以此来调用抽象类中的实例方法和实例属性;
public abstract class Person { //普通方法,属性 public int x = 10; public int y = 20; public void Test() { Console.WriteLine("11"); } //抽象 public abstract int x_ { get; set; } public abstract int y_ { get; set;} public abstract void Fun(); } public class Boy : Person { public override int x_ { set; get; } public override int y_ { get; set; } public override void Fun() { x_ = 10; Console.WriteLine(x_); } } static void Main(string[] args) { Person person = new Boy(); person.Test(); Boy b = new Boy(); b.Fun(); Console.ReadLine(); }
4.抽象类的作用
当我们在写基类时,有的属性、方法是一定要被重写的,在基类中实现并没有意义。这时我们就可以将这种属性、方法写作抽象属性、抽象方法,并将基类改作抽象类,这样我们在写派生类时,直接对没有实现的抽象属性、抽象方法进行重写(override)即可。
5.抽象类abstart与虚方法virtual的区别
1.虚方法(virtual)必须有方法体,抽象方法没有方法体;抽象方法是一种强制派生类覆盖的方法,否则派生类将不能被实例化;
2.抽象方法只能在抽象类中,虚方法不是;
3.抽象方法必然在派生类中重写,这一地点和接口类似,虚方法不需要在派生类中重写。
简单说:抽象方法是需要子类去实现的,而虚方法已经实现,可以被子类覆盖,也可以不被覆盖,取决于需求。都可为派生类提供重写;
接口类 Interface,抽象类和接口的区别
接口和抽象类比较像,都是等着其他类来现实接口或类;
当只有少数方法时,使用抽象类即可;
当有大量方法时,建议使用接口;
接口本身并不实现任何功能,它只是和声明实现该接口的对象订立了一个必须实现哪些行为的契约;
抽象类不能实例化,但是允许派生出具体,具有实际功能的类;
接口之间是可以继承与被继承的;
接口继承之后,需要实现接口,因此接口实现方法名称需要其方法名一样;
//------------------------------------------------------------------------------------------------
相同点:
1.都可以被继承
2.都不能被实例化
3.都可以包含方法声明
4.派生类必须实现未实现的方法
不同点:
1.抽象类可以定义字段,属性,方法实现;接口可以定义属性,索引器,事件,和方法声明,不能定义字段;
2.抽象类是一个不完整的类,需要进一步细化,而接口是一种行为规范。本身并不实现任何功能;抽象时允许派生出类的。
3.接口可以被多重实现,抽象类只能被单一继承。
4.抽象类更多的是定义在一系列紧密相关的类间;而接口大多数是关系疏松但都实现某一功能的类中;
5.抽象类是从一系列的相关对象中抽象出来的,因此反映的事务的内部共性;而接口是为了满足外部调用而定义的一个功能约定,因此反映的是外部特性。
6.接口可以支持回调,而继承不具备这个特点;
7.如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象类方法而不必实现,而在抽象类的子类中实现接口的方法。
定义接口
接口类使用Inteface关键字定义;
public interface interface1 { public void Add(int x,int y) { } public void Add(int x,int y,int z); }
接口里面只能有属性,方法,索引器,事件;
一般来说接口中是不建议方法有方法体;
接口不能实例化,也不能通过接口本身互访,必须需要被实现类继承
实现接口
当接口2继续接口1,实现类再继承接口2,实现类需要显示接口1和2的成员;
切记:实现类实现接口类,不是继承关系,是显示接口成员;
internal class Class2 : interface2 { public void Add(int x, int y, int z) { } public void Add2() { }
接口和类的区别
1.接口本身并不实现任何功能,但抽象类是允许派生出具体,具有实际功能的类;
2.接口用于规范,抽象类用于共性。抽象是类只能被单继承,但接口却可以一次实现多个;
3.接口的实例是实现接口的类给的;抽象类的实例是它的子类给的;
4.在抽象类中加入一个方法,那么它的子类就同时拥有这个方法;而在接口中加入一个方法,那么实现类就要重新编写;
5.接口成员本定义为公共的;而抽象类的成员有私有的,共有的,内部的等;
6.接口不能包含字段,构造函数,静态成员和常量;
7.接口支持回调,抽象类不支持实现回调,因为继承不支持;
8.接口可以可以作为值类型和引用类型,抽象类只能作用于引用类型。如:Struct就可以继承接口,而不能继承类;
多态
多态:一个接口多个功能。
静态多态性:编译时发生函数响应(调用);
动态多态性:运行时发生函数响应。
静态绑定(早期绑定):编译时函数和对象的连接机制。 两种技术实现静态多态性:函数重载/运算符重载。
函数重载:在同一范围内对相同函数名有多个定义,可以是参数类型或参数个数的不同,但不许只有返回值类型不同。
运算符重载:
关键字 abstract 声明抽象类:用于接口部分类的实现(派生类继承抽象类时,实现完成)。抽象类包含抽象方法,抽象方法可被派生类实现。
抽象类规则:
- 1.不能创建抽象类的实例
- 2.不能在抽象类外定义抽象方法
- 3.不能把抽象类声明为sealed(类前带关键字sealed代表该类是密封类,不能被继承)
关键字virtual声明虚方法:用于方法在继承类中的实现(在不同的继承类中有不同的实现)。
抽象类和虚方法共同实现动态多态性。
注:继承类中的重写虚函数需要声明关键字 override,在方法参数传入中写(类名 形参名)例如 public void CallArea(Shape sh),意思是传入一个 shape 类型的类。
重载和重写
1、重载(overload): 在同一个作用域(一般指一个类)的两个或多个方法函数名相同,参数列表不同的方法叫做重载,它们有三个特点(俗称两必须一可以):
- 方法名必须相同
- 参数列表必须不相同
- 返回值类型可以不相同
public void Sleep() { Console.WriteLine("Animal睡觉"); } public int Sleep(int time) { Console.WriteLine("Animal{0}点睡觉", time); return time; }
2、重写(override):子类中为满足自己的需要来重复定义某个方法的不同实现,需要用 override 关键字,被重写的方法必须是虚方法,用的是 virtual 关键字。它的特点是(三个相同):
- 相同的方法名
- 相同的参数列表
如:父类中的定义:、
public virtual void EatFood() { Console.WriteLine("Animal吃东西"); }
子类中的定义:
public override void EatFood() { Console.WriteLine("Cat吃东西"); //base.EatFood(); }
虚方法
虚方法:即为基类中定义的允许在派生类中重写的方法,使用virtual关键字定义。如:
public virtual void EatFood() { Console.WriteLine("Animal吃东西"); }
注意:虚方法也可以被直接调用。如:
Animal a = new Animal(); a.EatFood();
执行输出结果为:
Animal吃东西
虚方法的调用:调用上,使用子类构造的对象调用虚方法,就会调用子类的方法,使用父类构造的对象,就会调用父类的方法。
密封类
1.封装关键字 sealed
为了让一个类无法继承;
密封类特征:
密封类无法被继承,即不能为父类(静态类也不能被继承)
密封类不能继承静态类,可以继承抽象类;
不能有虚成员;
密封类可以有保护成员,但不建议使用,因为受保护成员时可以在子类中使用,二密封类没有子类;
只有重写才能被密封;(即密封类中不能直接定义密封方法,需要对重写的方法密封),而重写则说明需要继承一个父类;
密封类中定义方法的前提:
密封方法必须时对重写的方法进行密封;
因为要有重写,所以必须要有父类
部分类
- partial 关键字允许把类、结构、方法或者接口放在多个文件中。
- 一般情况下,某种类型的代码生成器生成了一个类的某部分,所以把这类放在多个文件中是有益的。
- 处理大型项目的时候,使用一个类分布于多个独立文件中可以让多位程序员同时对该类进行处理(相当于支持并行处理,很实用)
- 假定要给类添加一些从工具中自动生成的内容。如果重新运行该工具,前面所做的修改就会丢失。
- partial 关键字有助于把类分开放在两个文件中,而对不由代码生成器定义的文件进行修改。
- partial 关键字的用法是:把Partial放在class、struct、或者inteface关键字前面。在下面的举例中 SampleClass类驻留在两个不同的源文件 SampleClassAutogenerated.cs 和 SampleClass.cs中。
在使用partial需要注意以下一些情况:
1、使用partial 关键字表明可在命名空间内定义该类、结构或接口的其他部分;
2、所有部分都必须使用partial 关键字;
3、各个部分必须具有相同的可访问性,如public、private 等;
4、如果将任意部分声明为抽象的,则整个类型都被视为抽象的;
5、如果将任意部分声明为密封(sealed修饰符)的,则整个类型都被视为密封的;
6、如果任意部分声明继承基类时,则整个类型都将继承该类;
7、各个部分可以指定不同的基接口,最终类型将实现所有分部声明所列出的全部接口;
8、在某一分部定义中声明的任何类、结构或接口成员可供所有其他部分使用;
//SampleClassAtuogenerated.cs partial class SampleClass { public void MethodOne(){} } //SampleClass.cs partial class SampleClass { public void MethodTwo(){} }
编译包含这两个源文件项目时,会创建一个SampleClass类,它有两个方法MethodOne() 和 MethodTwo()。
如果声明类时 使用了下面的关键字,则这些关键字就必须应用于 同一个类型的的所有部 分。
1、public 2、private 3、protected 4、internal 5、abstract 6、sealed 7、new 8、一般约束
在嵌套的类型中,只要partial关键字位于class关键字的前面,就可以嵌套部分类。在把部分类编译到类型中时,属性,XML注释,接口,泛型类型的参数属性和成员会合并。有如下两个源文件:
//SampleClassAutogenerated.cs [CustomAttribute] partial class SampleClass:SampleBaseClass,IsampleClass { public void MethodOne(){} } //SampleClass.cs [AnotherAttribute] partial class SampleClass:IOtherSampleClass { public void MethodTwo(){} }
编译后,等价的源文件变成:
[CustomAttribute] [AnotherAttribute] partial class SampleClass:SampleBaseClass,ISampleClass,IOtherSampleClass { public void MethodOne(){} public void MethodTwo(){} }
注意:
尽管partial 关键字很容易创建跨多个文件的巨大的类,且不同的开发人员处理同一个类的不同文件,但是关键字并不用于这个目的。在这种情况下,最好把大类拆分成几个小类,一个类只用于一个目的。部分类可以包含部分方法。如果生成的代码应该调用可能不存在的方法,这就是非常有用的。
扩展部分类的程序员可以决定创建部分方法的自定义实现代码,或者什么也不做。
下面的代码片段包含一个部分类,其方法MethodOne调用APartialMethod方法。APartialMethod方法用partial关键字声明:因此不需要任何实现代码。如果没有实现代码,编译器将删除这个方法调用:
//SampleClassAtuogenerated.cs partial class SampleClass { public void MethodOne() { APartiaMethod(); } public partial void APartialMethod(); }
部分方法在实现可以放在部分类的任何其他地方,如下面的代码片段所示。
有了这个方法,编译器就在MethodOne内创建代码,调用这里声明的APartialMethod:
//SampleClass.cs partial class SampleClass : IOtherSampleClass { public void APartilMethod() { //implementation of APartialMethod } }
部分方法必须是void类型,否则编译器在没有实现了代码的情况下无法删除调用。