CSharp(C#)语言_第四章(类的深入理解)


本章节是对C#中类的深入学习,并能够掌握和运用

4.1 类成员

        类的成员大体分为两种:

数据成员 函数成员
字段、 常量 方法、 函数、 构造函数、 析构函数、 运算符、 索引、 事件

4.2 成员修饰符的顺序

多个修饰符可以在一起使用,自然就产生一个问题:它们需要按什么顺序排列呢?
类成员声明语句由下列部分组成:核心声明一组可选的修饰符一组可选的特性(attribute)。用于描述这个结构的语法如下。方括号表示方括号内的成分是可选的
        [特性] [修饰符] 核心声明

    修饰符:
        ■ 如果有修饰符,必须放在核心声明之前
        ■ 如果有多个修饰符,可以是任意顺序

    特性:
        ■ 如果有特性,必须放在修饰符和核心声明之前
        ■ 如果有多个特性,可以是任意顺序

4.3 实例类成员

        类成员可以关联到类的一个实例,也可以关联到类的整体,即所有类的实例。默认情况下,成员被关联到一个实例。可以认为是类的每个实例拥有自己的各个类成员的副本,这些成员称为实例成员
        改变一个实例字段的值不会影响任何其他实例中成员的值

静态成员 需要使用 static 标识符声明

4.4 静态字段

        除了实例字段,类还可以拥有静态字段

    ■ 静态字段被类的所有实例共享,所有实例都访问同一内存位置。因此,如果该内存位置的值被一个实例改变了,这种改变对所有的实例都可见
    ■ 可以使用static修饰符将字段声明为静态

4.5 从类的外部访问静态成员

        语法:类名.成员名

4.5.1 静态成员的生存周期

静态成员的生命周期与实例成员不同

    ■ 实例成员,只有在实例创建之后才产生实例成员,在实例销毁之后实例成员也就不存在了
    ■ 但即使类没有实例,也存在静态成员,并且可以访问

如果静态字段有初始化语句,那么会在使用该类的任何静态成员之前初始化该字段,但没必要在程序执行的开始就初始化

4.6 静态函数成员

除了静态字段,还有静态函数成员
        ■ 如同静态字段,静态函数成员独立于任何类实例。即使没有类的实例,仍然可以调用静态方法
        ■ 静态函数成员不能访问实例成员。然而,它们能访问其他静态成员

4.7 其他静态类成员类型

static 生命的静态成员
字段、 类型、 方法、 属性、 构造函数、 运算符、 事件

4.8 成员常量

        与本地常量类似,用于初始化成员常量的值在编译时必须是可计算的,而且通常是一个预定义简单类型或由它们组成的表达式

4.9 常量和静态

        成员常量比本地常量更有趣,因为它们表现得像静态值。它们对类的每个实例都是“可见的”,而且即使没有类的实例也可以使用。与真正的静态量不同,常量没有自己的存储位置,而是在编译时被编译器替换。这种方式类似于C和C++中的#define值
虽然常量成员表现得的一个静态量,但是不能将常量声明为static

4.10 属性

        属性是代表类的实例或类中的一个数据项的成员。使用属性看起来非常像写入或读取一个字段,语法是相同的

属性具有如下特征:
    ■ 它是命名的类成员
    ■ 它有类型
    ■ 它可以被赋值和读取(属性是函数成员
    ■ 它不为数据存储分配内存
    ■ 它执行代码(属性是指定的一组两个匹配的、诚为访问器的方法
    ■ set访问器为属性赋值
    ■ get访问器从属性获取值

4.10.1 属性声明和访问器

        setget访问器有预定义的语法和语义。可以把set访问器想象成一个方法,带有单一的参数“设置”属性的值。get访问器没有参数并从属性返回一个值

    set访问器总是:
        ■ 拥有一个单独的、隐式的值参,名称为value,与属性的类型相同
        ■ 拥有一个返回类型void
    get访问器总是:
        ■ 没有参数
        ■ 拥有一个与属性类型相同的返回类型

        set访问器中的隐式参数value是一个普通的值参。和其他值参一样,可以用它发送数据到方法体或这种情况中的访问器块。在块的内部,可以像普通变量那样使用value,包括对它赋值

访问器的其他重点:
        ■ get访问器的所有执行路径必须包含一条return语句,返回一个属性类型的值
        ■ 访问器setget可以以任何顺序声明,并且,除了这两个访问器外在属性上不允许有其他方法

4.10.2 使用属性

就像之前看到的,写入和读取属性的方法与访问字段一样。访问器被隐式调用
        要写入一个属性,在赋值语句的左边使用属性的名称
        要读取一个属性,把属性的名称用在表达式中

4.10.3 只读和只写属性

要想不定义属性的某个访问器,可以忽略该访问器的声明

        ■ 只有get访问器的属性称为只读属性。只读属性是一种安全的,把一项数据从类或类的实例中传出,而不允许太多访问方法
        ■ 只有set访问器的属性称为只写属性。只写属性是一种安全的,把一项数据从类的外部传入类,而不允许太多访问的方法
        ■ 两个访问器中至少有一个必须定义,否则编译器会产生一条错误信息

4.10.4 属性与公共字段

按照推荐的编码实践,属性比公共字段更好,理由如下:

        ■ 属性是函数型成员而不是数据成员,允许你处理输入和输出,而公共字段不行
        ■ 属性可以只读或只写,而字段不行
        ■ 编译后的变量和编译后的属性语义不同

        如果要发布一个由其他代码引用的程序集,那么第二点将会带来一些影响。例如,有的时候开发人员会有用公共的字段代替属性的冲动,因为如果以后需要为字段的数据增加处理逻辑的话可以再把字段改为属性。这没错,但是如果那样修改的话,所有访问这个字段的其他程序集都需要重新编译,因为字段和属性在编译后的语义不一样。另外,如果实现的是属性,那么只需要修改属性的实现,无需重新编译访问它的其他程序集

4.10.5 自动实现属性

因为属性经常被关联到后备字段,C#提供了自动实现属性(automatically implemented property或auto-implemented property),允许只声明属性而不声明后备字段。编译器会为你创建隐藏的后备字段,并且自动挂接到get和set访问器上

自动实现属性的要点如下:
        ■ 不声明后备字段——编译器根据属性的类型分配存储
        ■ 不能提供访问器的方法体——它们必须被简单地声明为分号。get担当简单的内存读,set担当简单的写
        ■ 除非通过访问器,否则不能访问后备字段。因为不能用其他的方法访问它,所以实现只读和只写属性没有意义,因此必须同时提供读写访问器

语法:访问修饰符 数据类型 属性名 ={ set; get;}

4.10.6 静态属性

属性也可以声明为static。静态属性的访问器和所有静态成员一样,具有以下特点:

        ■ 不能访问类的实例成员——虽然它们能被实例成员访问
        ■ 不管类是否有实例,它们都是存在的
        ■ 当从类的外部访问时,必需使用类名引用,而不是实例名

4.11 实例构造函数(方法)

实例构造函数是一个特殊的方法,它在创建类的每个新实例时执行

        ■ 构造函数用于初始化类实例的状态
        ■ 如果希望能从类的外部创建类的实例,需要将构造函数声明为public

除了下面这几点,构造函数看起来很像类声明中的其他方法:
        ■ 构造函数的名称和类名相同。
        ■ 构造函数不能有返回值。

语法:

class Myclass
{
	public Myclass()  //构造方法名必须与类名相同    public 可换成其它访问修饰符
	{
		//方法体
		...
	}
	...
}

构造方法是可以带参数的,参数数据类型、数量不同,就是不同的构造方法

4. 12静态构造函数

        构造函数也可以声明为static。实例构造函数初始化类的每个新实例,而static构造函数初始化类级别的项。通常,静态构造函数初始化类的静态字段

初始化类级别的项:
    ■ 在引用任何静态成员之前
    ■ 在创建类的任何实例之前

静态构造函数在以下方面与实例构造函数类似:
    ■ 静态构造函数的名称必须和类名相同
    ■ 构造函数不能返回值

静态构造函数在以下方面和实例构造函数不同:
    ■ 静态构造函数声明中使用static关键字
    ■ 类只能有一个静态构造函数,而且不能带参数
    ■ 静态构造函数不能有访问修饰符

注意:

类既可以有静态构造函数也可以有实例构造函数
如同静态方法,静态构造函数不能访问所在类的实例成员,因此也不能使用this访问器

不能从程序中显式调用静态构造函数,系统会自动调用它们:
    ■ 类的任何实例被创建之前
    ■ 类的任何静态成员被引用之前

4.13 对象初始化语句

对象创建表达式由关键字new后面跟着一个类构造函数及其参数列表组成。对象初始化语句扩展了创建语法,在表达式的尾部放置了一组成员初始化语句。这允许你在创建新的对象实例时,设置字段和属性的值。
该语法有两种形式,如下面所示。一种形式包括构造函数的参数列表,另一种不包括。注意,第一种形式甚至不使用括起参数列表的圆括号
new TypeName { FieldOrop = InitExpr, FieldOrop = InitExpr, ... }
new TypeName(ArgList) { FieldOrop = InitExpr, FieldOrop = InitExpr, ... }

注意
        创建对象的代码必须能够访问要初始化的字段和属性
        初始化发生在构造方法执行之后,因此在构造方法中设置的值可能会在之后对象初始化中重置为相同或不同的值

4.14 析构函数

        析构函数(destructor)执行在类的实例被销毁之前需要的清理或释放非托管资源的行为。非托管资源是指通过Win32API获得的文件句柄,或非托管内存块。使用.NET资源是无法得到它们的,因此如果坚持使用.NET类,就不需要为类编写析构函数

4.15 readonly修饰符

    字段可以用readonly修饰符声明。其作用类似于将字段声明为const,一但值被设定就不能改变
        const字段只能在字段的声明语句中初始化,而readonly字段可以在下列任意位置设置它的值
            ■ 字段声明语句,类似const
            ■ 类的任何构造函数。如果是static字段,初始化必须在静态构造函数中完成
        const字段的值必须在编译时决定,而readonly字段的值可以在运行时决定。这种增加的自由性允许你在不同的环境或不同的构造函数中设置不同的值
        和const不同,const的行为总是静态的,而对于readonly字段以下两点是正确的
            ■ 它可以是实例字段,也可以是静态字段
            ■ 它在内存中有存储位置

4.16 this关键字

this关键字在类中使用,是对当前实例的引用。它只能被用在下列类成员的代码块中:
        ■ 实例构造函数
        ■ 实例方法
        ■ 属性和索引器的实例访问器
很明显,因为静态成员不是实例的一部分,所以不能在任何静态函数成员的代码中使用this关键字。更适当地说,this用于下列目的:
        ■ 用于区分类的成员和本地变量或参数
        ■ 作为调用方法的实参

4.17 索引

索引器就是一组getset访问器,与属性类似

4.17.1 索引器和属性

索引器和属性在很多方面是相似的
    ■ 和属性一样,索引器不用分配内存来存储。
    ■ 索引器和属性都主要被用来访问其他数据成员,它们与这些成员关联,并为它们提供获取和设置访问
①属性通常表示单独的数据成员。
②索引器通常表示多个数据成员。

注意

和属性一样,索引器可以只有一个访问器,也可以两个都有;
索引器总是实例成员。因此不能声明为static;
和属性一样,实现get和set访问器的代码不必一定要关联到某个字段或属性。这段代码
可以做任何事情或什么也不做,只要get访问器返回某个指定类型的值即可。

声明索引器的语法如下。请注意以下几点:
    ■ 索引器没有名称。在名称的位置是关键字this
    ■ 参数列表在方括号中间
    ■ 参数列表中必须至少声明一个参数

语法:

ReturnType this [ 参数列表 ] //参数列表必须有
{
	get
	{
		...
	}
	set
	{
		...
	}
}
4.17.2 索引器重载

只要索引器的参数列表不同,类就可以有任意多个索引器。索引器类型不同是不够的。这叫做索引器重载,因为所有的索引器都有相同的“名称”:this访问引用

注意类中的索引器重载必须有不同的参数列表

4.18 访问器的访问修饰符

        已经有两种函数成员带getset访问器:属性和索引器。默认情况下,成员的两个访问器有和成员自身相同的访问级别。也就是说,如果一个属性有public访问级别,那么它的两个访问器都有同样的访问级别,对索引也一样

        不过,你可以为两个访问器分配不同的访问级别。例如,如下代码演示了一个非常常见而且重要的例子,那就是为set访问器声明为private,为get访问器声明为publicget之所以是public的,是因为属性的访问级别就是public

访问器的访问修饰符有几个限制。最重要的限制如下:
    ■ 仅当成员(属性或索引器)既有get访问器也有set访问器时,其访问器才能有访问修饰符
    ■ 虽然两个访问器都必须出现,但它们中只能有一个有访问修饰符
    ■ 访问器的访问修饰符必须比成员的访问级别有更严格的限制性

4.19 分部类和分部类型

类的声明可以分割成几个分部类的声明:
    ■ 每个分部类的声明都含有一些类成员的声明
    ■ 类的分部类声明可以在同一文件中也可以在不同文件中
每个局部声明必须被标为partial class,而不是单独的关键字class。分部类声明看起来和普通类声明相同,除了那个附加的类型修饰符partial

组成类的所有分部类声明必须在一起编译。使用分部类声明的类必须有相同的含义,就好像所有类成员都声明在一个单独的类声明体内

4.20 分部方法

        分部方法是声明在分部类中不同部分的方法。分部方法的不同部分可以声明在不同的分部类中,也可以声明在同一个类中。分部方法的两个部分如下:

定义分部方法声明。
    ■ 给出签名和返回类型
    ■ 声明的实现部分只是一个分号

实现分部方法声明。
    ■ 给出签名和返回类型
    ■ 是以正常形式的语句块实现

关于分部方法需要了解的重要内容如下:
    ■ 定义声明和实现声明的签名和返回类型必须匹配。签名和返回类型有如下特征
    ■ 返回类型必须是void
    ■ 签名不能包括访问修饰符,这使分部方法是隐式私有的
    ■ 参数列表不能包含out参数
    ■ 在定义声明和实现声明中都必须包含上下文关键字partial,直接放在关键字void之前

可以有定义部分而没有实现部分。在这种情况下,编译器把方法的声明以及方法内部任何对方法的调用都移除。不能只有分部方法的实现部分而没有定义部分

发布了48 篇原创文章 · 获赞 55 · 访问量 4465

猜你喜欢

转载自blog.csdn.net/qq_43562262/article/details/104873381