主要是:封装、继承、多态、接口、抽象类、虚方法、访问修饰符、重载与重写、索引器...
继承
继承类型:实现继承与接口继承
接口继承表示一个类型只继承了函数的签名,没有继承任何实现代码。
C# 支持实现继承和接口继承。
多重继承:C#不支持多重实现继承,但是支持多重接口继承(这表示C#类可以派生自另一个类和任意多个接口)
构造函数执行顺序:编译器最先调用的是基类的构造函数(会初始化其成员变量)、再调用派生类的构造函数(其能访问 任何基类的方法、属性等,因为基类已经构造出来了)
类(class)与结构(struct)
构造函数:结构不能有默认的构造函数,因为结构的副本是由编译器创建和销毁的,所以不需要默认的构造函数和析构函数。
类型:结构是值类型,所以对结构变量所做的改变不会影响其原值,而类是引用类型,改变其变量的值会改变其原值。向方法传递结构时是通过值传递的,而不是通过引用。
内存:类属于引用类型,是分配在内存的堆上的;Struct属于值类型,是分配在内存的栈上的。
Class可以被实例化,类可以实现接口。Class默认成员访问为private的,而结构是public的。
抽象类和接口的区别
抽象类表示的是,这个对象是什么。是对根源的抽象。
接口表示的是,这个对象能做什么。是对动作的抽象。
当你关注一个事物的本质的时候,用抽象类;当你关注一个操作的时候,用接口。
比如,男人,女人,这两个类(如果是类的话……),他们的抽象类是人。
人可以吃东西,狗也可以吃东西,你可以把“吃东西”定义成一个接口,然后让这些类去实现它。
所以,在高级语言上,一个类只能继承一个类(抽象类)(正如人不可能同时是生物和非生物),但是可以实现多个接口(吃饭接口、走路接口)。
1、抽象类
如果一个类不与具体的事物相联系,而只是表达一种抽象的概念,仅仅是作为其派生类的一个基类,这样的类就是抽象类。
抽象类是abstract修饰符的,并且只能用作基类,但抽象类是可以实现接口的。
抽象类不能创建类的实例,然而可以创建一个变量,其类型是一个抽象类,并让它含有对非抽象类的实例的引用。不能有抽象构造函数或抽象静态方法。abstract类的子类为它们父类中的所有抽象方法提供实现,否则它们也是抽象类。抽象类不能被密封。
(1) 抽象方法只作声明,而不包含实现,可以看成是没有实现体的虚方法
(2) 抽象类不能被实例化
(3) 抽象类可以但不是必须有抽象属性和抽象方法,但是一旦有了抽象方法,就一定要把这个类声明为抽象类
(4) 派生类必须覆盖(override)基类的抽象方法
(5) 抽象派生类可以覆盖基类的抽象方法,也可以不覆盖。如果不覆盖,则其具体派生类必须覆盖它们。
2、接口
接口(interface)是抽象类的变体。接口中的所有方法都是抽象的,没有一个有程序体。
接口是可以继承接口的。
(1) 接口不能被实例化
(2) 接口只能包含方法声明,不能有程序体
(3) 接口的成员包括方法、属性、索引器、事件
(4) 接口中不能包含常量、字段(域)、构造函数、析构函数、静态成员(即不能有static)。
(5) 接口中的所有成员默认为public,但是不能显示写public,因此接口中不能有private修饰符
(6) 派生类必须实现接口的所有成员
(7) 一个类可以直接实现多个接口,接口之间用逗号隔开
(8) 一个接口可以有多个父接口,实现该接口的类必须实现所有父接口中的所有成员
3、异同
相同点:
(1) 都可以被继承
(2) 都不能被实例化
(3) 都包含未实现的方法声明
(4) 派生类必须实现未实现的方法
区 别:
(1) 抽象基类可以定义字段、属性、方法实现。接口只能定义属性、索引器、事件、和方法声明,不能包含字段。
(2) 抽象类是一个不完整的类,需要进一步细化,而接口是一个行为规范。微软的自定义接口总是后带able字段,证明其是表述一类“我能做。。。”
(3) 接口可以被多重实现,抽象类只能被单一继承
(4) 抽象类中可以定义成员的实现,但接口中不可以
(5) 抽象类是从一系列相关对象中抽象出来的概念, 因此反映的是事物的内部共性;接口是为了满足外部调用而定义的一个功能约定, 因此反映的是事物的外部特性
(6) 接口基本上不具备继承的任何具体特点,它仅仅承诺了能够调用的方法
(7) 接口可以用于支持回调,而继承并不具备这个特点
(8) 抽象类实现的具体方法默认为虚的,但实现接口的类中的接口方法却默认为非虚的,当然您也可以声明为虚的
(9) 如果抽象类实现接口,则可以把接口中方法映射到抽象类中作为抽象方法而不必实现,而在抽象类的子类中实现接口中方法
override与overload区别
overload:重载是方法的名称相同,但参数个数或参数类型不同,可以进行多次重载以适应不同的需求。仅返回类型不同的两个函数不是重载【这种情况直接报错:已定义了一个名为“xxx”的具有相同参数类型的成员,编译不通过】
override:是进行对基类中方法的重写。主要针对基类中标识virtual的虚函数 或者abstract的抽象函数。
虚方法:把一个基类函数声明为virtual,就可以在任何派生类中重写该函数(体现多态性)。virtual只对类中的实例函数成员才有意义,故成员字段和静态函数 不能声明为virtual。
访问修饰符
private : 私有成员, 在类的内部才可以访问。(类的成员、方法的默认)
protected : 保护成员,在该类内部和继承类中可以访问。
public : 公共成员,完全公开,没有访问限制。 (接口的默认修饰符,而且不能写)
internal: 在同一命名空间内可以访问。(类的默认修饰符)
C# 方法默认访问级别 : private
C# 类默认访问级别 : internal
1、命名空间下的元素的默认访问修饰符
public : 同一程序集的其他任何代码或引用该程序集的其他程序集都可以访问该类型或成员。
internal : 同一程序集中的任何代码都可以访问该类型或成员,但其他程序集不可以访问。
2、各类型中的成员的默认访问修饰符
剩下的修饰符主要是针对继承这个语言特性的,拥有继承的类型有两个类(class)和接口(interface)。public,internal同样可以用于类型成员。
private : 同一类和结构的代码可以访问该类型和成员。
protected : 同一类和派生(继承特性)类中的代码可以访问该类型和成员。
protected internal : 同一程序集中的任何代码或其他程序集中的任何派生类都可以访问该类型或成员。
接口(interface)
接口成员访问修饰符默认为public,且不能显示使用访问修饰符。
类(class)
构造函数默认为public访问修饰符。
析构函数不能显示使用访问修饰符且默认为private访问修饰符。
类的成员默认访问修饰符为private;
枚举(enum)
枚举类型成员默认为public访问修饰符,且不能显示使用修饰符。
结构(struct)
结构成员默认为private修饰符。
结构成员无法声明为protected成员,因为结构不支持继承。
嵌套类型
嵌套类型的默认访问修饰符为private。 和类,结构的成员默认访问类型一致。
索引器
索引器是一种特殊的类成员,它能够让对象以类似数组的方式来存取,使程序看起来更为直观,更容易编写。
1、索引器的定义
C#中的类成员可以是任意类型,包括数组和集合。当一个类包含了数组和集合成员时,索引器将大大简化对数组或集合成员的存取操作。
定义索引器的方式与定义属性有些类似,其一般形式如下:
[修饰符] 数据类型 this[索引类型 index] { get{//获得属性的代码} set{ //设置属性的代码} }
其中:
数据类型:是表示将要存取的数组或集合元素的类型。
索引器类型:表示该索引器使用哪一类型的索引来存取数组或集合元素,可以是整数,可以是字符串;
this:表示操作本对象的数组或集合成员,可以简单把它理解成索引器的名字,因此索引器不能具有用户定义的名称。 例如:
class Z { //可容纳100个整数的整数集 private long[] arr = new long[100]; //声明索引器 public long this[int index] { get { //检查索引范围 if (index < 0 || index >= 100) { return 0; } else { return arr[index]; } } set { if (!(index < 0 || index >= 100)) { arr[index] = value; } } }
2、索引器的使用
一般形式如下:对象名[索引]
其中索引的数据类型必须与索引器的索引类型相同。例如:
Z z=new z(); z[0]=100; z[1]=101; Console.WriteLine(z[0]);
C#中并不将索引器的类型限制为整数。例如,可以对索引器使用字符串。通过搜索集合内的字符串并返回相应的值,可以实现此类的索引器。由于访问器可以被重载,字符串和整数版本可以共存。
class DayCollection { string[] days={"Sun","Mon","Tues","Wed","Thurs","Fri","Sat"}; private int GetDay(string testDay) { int i=0; foreach(string day in days) { if(day==testDay) return i; i++; } return -1; } public int this[string day] { get{return (GetDay(day))} } } static void Main(string[] args) { DayCollection week=new DayCollection(); Console.WriteLine("Fri:{0}",week["Fri"]); Console.WriteLine("ABC:{0}",week["ABC"]); }
3、接口中的索引器
在接口中也可以声明索引器,接口索引器与类索引器的区别有两个:
一是接口索引器不使用修饰符;
二是接口索引器只包含访问器get或set,没有实现语句。
访问器的用途是指示索引器是可读写、只读还是只写的,如果是可读写的,访问器get或set均不能省略;如果只读的,省略set访问器;如果是只写的,省略get访问器。
例如:
public interface IAddress { string this[int index]{get;set;} string Address{get;set;} string Answer(); }
表示所声明的接口IAddress包含3个成员:一个索引器、一个属性和一个方法,其中,索引器是可读写的。
4、索引器与属性的比较
索引器与属性都是类的成员,语法上非常相似。索引器一般用在自定义的集合类中,通过使用索引器来操作集合对象就如同使用数组一样简单;而属性可用于任何自定义类,它增强了类的字段成员的灵活性。
属性 |
索引器 |
允许调用方法,如同公共数据成员 |
允许调用对象上的方法,如同对象是一个数组 |
可通过简单的名称进行访问 |
可通过索引器进行访问 |
可以为静态成员或实例成员 |
必须为实例成员 |
其get访问器没有参数 |
其get访问器具有与索引器相同的形参表 |
其set访问器包含隐式value参数 |
除了value参数外,其set访问器还具有与索引器相同的形参表 |