《C#高级编程》学习笔记,阶段整理(对象和类型)
对象和类型
类(class)是C#开发过程中最经常接触到的东西,类中可能包含有字段、常量、属性、方法、构造函数、索引器、事件、运算符、析构函数、内部类。以及我们可以使用多种关键字来辅助描述它达到我们希望它所拥有的功能。与类相似的还有结构(struct),在下面将会进行具体的描述。
1、类和结构的区别:
所有的类都是派生于一个名为Object的基类,就算是我们自己定义的类如果在定义时没有指定基类系统将隐式的将Object类作为它的基类。这对于结构来说是一样的,每个结构派生自ValueType类,而ValueType则又派生于Object类。
结构与类的创建方式相类似例如有个类的代码如下:
public class Dimensions { public double Length{get;set;} public double Width{get;set;} public Dimensions(double length,double width){Length = length; Width = width;} }
而我们需要定义一个结构只需要将class更改为struct
public struct Dimensions { public double Length{get;set;} public double Width{get;set;} public Dimensions(double length,double width){Length = length; Width = width;} }
由此我们可以看出结构与类十分相似,但也有很明显的不同之处:
1》首先,尽管在语法上可以将结构当做类来处理,但结构是值类型的也就是说结构所赋的值是真正的值而不是引用,结构的值放置在栈中而不是指向堆也不会再堆中占用内存,这使结构在性能上具有优势。而类所实例化的对象储存的是一个引用地址,当使用new语句时,系统将在托管堆空间中申请内存存放一个对象,并将这个对象的引用地址给类声明的变量。
2》其次,结构不是为继承而被创建出来的,所以结构并不支持继承。但其实结构是派生于Object类的,所以结构也可以使用Object中的方法。
3》还有,结构的初始化不同于类的初始化,例如:
Dimensions dimension; dimension.Length=100;
结构可以像基本数据类型一样被声明然后赋值,但若是个类,这将报没有初始化的异常。这就使得结构的构造函数与类的构造函数的方式也存在不同。
4》此外,结构可以指定字段在内存中如何布局,这将在后面的随笔中做详细说明。
扩展:
2、类成员:
1)字段:即类中包含的变量,一般声明为私有。
2)常量:类中的常量,定义常量有助于提高代码的可读性,也可以帮助约束某些不允许改变的字段。
3)属性:公开字段的方式,对于类来说它是方法,而对于客户端来说它是字段。可包含get,set方法。如果仅有get方法则说明这个属性所对应的字段是只读的。
4)索引器:索引器运行类像数组一样被访问,它的定义有点像属性,通过get,set方式获取指定下标的值和赋值。
5)构造函数:构造函数的定义与方法类似,但不需要定义返回类型,构造函数一般在类被实例化时被用于初始化对象。如果没有定义构造函数,系统将隐式的生成一个无参构造函数。
6)析构函数:在构造函数定义时在构造函数名称前加上~符号就是析构函数了。例如类名为Dimensions 构造函数为 Dimensions(){},析构函数为~Dimensions(){},析构函数在对象被回收时执行,但谁也无法保证析构函数什么时候执行,因为对象被回收时间是不确定的。
7)方法:方法就是包含执行动作的函数。
8)类型:类可以包含内部类。
9)运算符:运算符可以在类中重载,即原本我们无法将两个相同类的实例对象进行相加,但定义运算符重载后,我们可以对实例对象进行我们自定义的运算方式。
10)事件:事件使用 发布-订阅(publisher-subscriber) 模型,事件可以理解为当某件特定的事发生时,触发响应事件。需要在类中实现定义事件的触发方法,执行的动作。
3、表达式体成员:
在使用C#7编程时,属性访问器可以写成具有表达式体的属性访问器:
private int _length; private int Length { get=>_length; set=>_length=value; }
使用具有表达式体的成员时,属性访问器写成一条语句,简化了属性访问器声明的代码量。
同时我们也可以写表达式体的构造函数,表达式体的方法。
4、按值和按引用传递参数
结构是按值传递的代表,我们使用结构定义的变量进行操作时所传递的是储存在堆栈中的值,这有一点好处就是为结构分配内存时非常的快,但当把结构作为参数进行传递时,结构内的所有参数将被复制,也就是说传递的结构与原本的结构变成了两个独立的存在,多占用了内存。此时我们可以使用ref关键字将其声明为引用传递,仅传递引用,节省了空间,但是如果这么做,当引用结构被改变,原结构中的内容也将改变。
5、方法重载
在类中的方法我们可以使用重载来进行扩展,只需要相同的方法名所携带的参数数量或者类型不相同就可以进行自定义的重载,编辑器会根据传入的参数数量以及类型进行确定客户端使用的是哪一个重载方法。
6、构造函数和静态构造函数
类中允许自定义构造函数(如果不存在自定义构造函数,编译器将自己创建一个无参构造函数,如果自定义构造函数存在,则编译器则认为自定义构造函数为唯一的构造函数就不创建无参构造函数了,不管无参构造函数存不存在)。我们也可以对构造函数进行重载,这是被允许的。构造函数的存在是为了当客户端程序使用new关键字在托管堆中创建对象后执行的操作,只要创建类对象就会执行它。
一个类中在存在有构造函数后也可以声明一个静态构造函数,这并不算重载因为类的构造函数是在类创建对象时所进行的而静态构造函数是在引用类之前被调用,它只被调用一次而且无法确定调用时间,一般来说它是为了给类中静态字段或属性进行初始化。静态构造函数不能带有任何参数而且只能访问静态变量。另一方面,如果静态字段由默认值,则在执行静态构造函数之前先分配静态字段的初始化值内存空间。
7、只读字段
如果一个成员字段不允许被改变,那么你就应该考虑将其声明为readonly只读,只读属性将保证内容不被改变,在方法体中若不希望值传递的过程中数据不会改变那么可以使用in关键字。属性也可以仅公开get方法来达到数据不被改变的结果。
8、枚举
enum类型可以声明一个枚举类型,在使用花括号中使用逗号隔开枚举的内容,枚举类型包含一组常量,可以使用枚举类型所定义的属性名使用点.来访问枚举常量,也可以进行强制类型转换将其变成数字,数字是表示此枚举常量在枚举中的位置。
9、部分类与扩展方法
partial关键字可以在声明类时使用,它允许把类,结构,方法或接口放置在多个文件当中以相同的名称作为声明名,这有助于可读性提高和开发,当运行时,编译器会将它们全部合并成一个完整的类(结构或者接口),特别注意的是部分方法返回类型必须为void因为编译器在没有实现代码情况下无法删除调用、
10、静态类
静态类不需要被实例化就可以被调用,静态内中的方法属性字段等都是静态的,可以直接进行访问(如果访问权限是public的话),而且只有类可以声明成静态的,静态类不可以被继承也不可以实现接口。
11、Object类
System.Object类是所有类的基类,或直接或间接。Object类中的所有方法,在其他类中都可以进行调用或者重写。