3.1 面向对象简介
面向对象不仅是一项具体的软件开发技术,也是一种符合人类思维习惯的编程思想。现实生活中存在各种形态不同的事物,这些事物之间存在着各种各样的联系。面向对象编程(object-oriented programming,OOP)就是利用对象模型技术分析目标问题,抽象出相关对象的共性,并对共性进行分类及分析各类之间的关系,同时使用类描述同一类问题。
面向对象中类的定义充分体现了抽象数据类型的思想,基于类的体系结构可以把程序的修改局部化,特别是一旦系统功能需求要修改时,只要修改类中间的某些操作,而类所代表的对象基本不变,保持整个系统仍然稳定。
3.2 类与对象
面向对象的编程思想力图使程序对事物的描述与该事物在现实中的形态保持一致,为了做到这一点,在面向对象的思想中提出了两个概念,即类和对象。
类是对某一类事物的抽象描述,对象是该类事物的某一个实体,对象会被分配物理内存。
3.2.1 类的声明
创建对象,首先需要声明一个类,用于描述一组对象的特征和行为。类中可以定义字段、属性、方法等成员。定义在类中的变量称为字段,字段用于在类中存储数据,属性用于描述对象的特征,而方法用于描述对象的行为。
注意:若类的声明中没有指定字段的初始值,使用对象时也没有给字段赋值,则编译时会自动赋予其类型的默认值并发出警告。
声明类的语法格式:
[访问修饰符] class 类名称 [: 基类或接口]
{
类成员定义
}
代码实例
3.2.2 对象的创建与使用
1) 对象的创建
语法格式:
类名 对象名称 = new 类名();
Person p = new Person();
new Person()创建一个对象并对其初始化
p是一个引用,指向new Person()创建出来的对象
参考:https://blog.csdn.net/yl2isoft/article/details/23521083
2) 对象的使用
语法格式:
对象引用.对象成员
p.Speak();
3.3 类的数据成员
类的成员包括:类的常量、字段、属性、索引器、方法、事件、构造方法等,其中常量、字段、属性都是与类的数据有关的成员。
1)常量
在类中的常量是一种符号常量,符号常量是由用户根据需要自行创建的常量,可能需要反复使用。
2)字段
字段表示类的成员变量,字段的声明方式即变量的声明方式,字段的值代表某个对象数据状态,字段使类具备封装数据的能力,一般情况下,应将字段声明为private,然后通过属性或方法访问其内容。
3)属性
在C#中,为保证类中内部的数据的安全,可以使用属性封装字段,首先需要将字段访问级别设置为private,再通过属性的get和set访问器对字段进行读写操作,具体语法格式如下:
[访问修饰符] 数据类型 属性名
{
get{获得属性的代码;}
set{设置属性的代码;}
}
如果设置读写属性,需要同时使用get和set访问器;
如果设置只读属性,只需要使用get访问器,一般用于在构造方法中给属性赋值,在程序运行过程中不能修改该属性的值;
如果设置只写属性,需要使用set访问器,在程序运行过程中只能写入值而不能读取值;
如果设置自动属性,则不需要书写任何属性的代码,也就是在get和set访问器后面不加大括号,直接加”;”即可。
注意:在自动属性中,无须定义一个相应的私有字段,也不必写任何return和value语句。自动属性虽然简洁,但不能通过属性 完成任何更多的复杂逻辑。
代码实例
3.4 方法
在类中自定义的“函数”称为“方法”。方法是表示实现类功能而执行的计算或操作。
3.4.1 方法的定义与调用
每个方法都有一个名称和一个主体。方法名是一个有意义的标识符,用来描述方法的用途;方法主体包含了调用方法时实际执行的语句。定义方法的语法格式如下:
[访问修饰符] 返回值类型 方法名(参数列表)
{
方法体
[return 返回值;]
}
上述语法需要注意以下几点:
(1)方法的返回类型是指调用方法后 返回值的类型;
(2)参数可有可无,参数列表中的形参 和 实际调用时传递的实参,一一对应,个数和数据类型必须一致;
(3)方法调用有3种:
1) 在同一类中,方法可以直接调用;
2) 在其它类中,需要通过类的实例进行调用;
3) 静态方法需要通过类名进行调用。
代码实例
3.4.2 方法的重载
方法重载是一种操作性多态。当需要在 多个不同的实现 中 对不同的数据 进行 相同的逻辑操作时,就可以使用重载。
方法重载指在同一个类中创建多个同名的方法,但这些方法的参数互不相同,可以是参数类型不同,也可以是参数个数不同。决定方法 是否构成重载的3个条件如下:
(1)在同一个类中;
(2)方法名相同;
(3)参数列表不同。
代码实例
3.4.3 方法的高级参数
为了让方法的调用更加灵活,C#中引入了高级参数的概念。通过高级参数可以实现两个功能,一是调用方法时允许传入任意个数的参数,而不受形参个数的约束;二是允许在方法中修改方法以外变量的值。高级参数有以下3种:
(1)params参数:用于实现方法接收任意个数的参数;
(2)ref参数:用于传递参数的引用,而不是参数的值;
(3)out参数:用于将值从方法体内传到方法体外。
1. params参数
params参数在定义时不需要确定参数的个数,可以使用params关键字加上数组的方式作为方法的参数,在调用方法时传入任意个数类型相同的参数即可。
注意:
(1) 一个方法中最多只能出现一个params;
(2) params关键字只能放到所有参数的最后面,即params修饰的参数后面不能再有其他参数;
(3) 当参数为params修饰时,要防止外界传入非法参数,如null。
2. ref参数
在使用引用传递时,需要在参数前面加上ref关键字,并在调用方法时也使用ref关键字,就可以实现当方法执行后,外面的变量值保持方法内修改后的结果
3. out参数
out参数也用于实现引用传递,使用out参数可以将值从方法体内传到方法体外。
4. ref和out区别
参考:https://www.cnblogs.com/sunliyuan/p/5999045.html
ref和out的区别在C# 中,既可以通过值也可以通过引用传递参数。通过引用传递参数允许函数成员更改参数的值,并保持该更改。若要通过引用传递参数, 可使用ref或out关键字。ref和out这两个关键字都能够提供相似的功效,其作用也很像C中的指针变量。
它们的区别是:
1、使用ref型参数时,传入的参数必须先被初始化。对out而言,必须在方法中对其完成初始化。
2、使用ref和out时,在方法的参数和执行方法时,都要加Ref或Out关键字。以满足匹配。
3、out适合用在需要retrun多个返回值的地方,而ref则用在需要被调用的方法修改调用者的引用的时候。
out:
方法参数上的 out 方法参数关键字使方法引用传递到方法的同一个变量。当控制传递回调用方法时,在方法中对参数所做的任何更改都将反映在该变量中。
当希望方法返回多个值时,声明 out 方法非常有用。使用 out 参数的方法仍然可以返回一个值。一个方法可以有一个以上的 out 参数。
若要使用 out 参数,必须将参数作为 out 参数显式传递到方法。out 参数的值不会传递到 out 参数:。
不必初始化作为 out 参数传递的变量。然而,必须在方法返回之前为 out 参数赋值。
属性不是变量,不能作为 out 参数传递。
ref是 有进有出,而out是 只出不进。
5. 形参 实参 引用间的关系
发生函数调用的时候,有 按值传递 和 按引用传递 这两种方式。对于按值传递是将实参的值拷贝给形参,虽然此时形参和实参的值相同,但是形参和实参分别占据不同的内存空间,所以一旦函数调结束,形参占据的内存空间就会释放,故方法中对形参的任何改变都不会影响实参的值。而ref和out都是引用类型,所以属于按引用传递,对于按引用传递的方式,形参和实参共享同一块内存空间,故在方法中对形参的改变,其实就是对实参值的改变,即使函数调用结束,方法中对形参的改变即是对实参的改变,这种改变在函数调用结束后仍旧保持。
由于ref和out修饰的参数同属按引用传递,所以对于函数重载,ref和out类型是同一种类型;
不使用ref和out修饰的参数,不一定就是按值传递的。例如,数组、集合等都是引用类型,故不必使用ref修饰,也是按引用传递的。
6. 代码实例
3.5 构造方法
构造函数时类的一个特殊成员,会在类实例化对象时自动调用,为对象开辟内存空间,并对类中的成员进行初始化。
C# 中每个类至少有一个构造方法,当声明一个类时,若没有定义构造方法,系统会自动添加一个默认的没有参数的构造方法,在其方法体内没有任何代码,即什么也不做。反之,若声明类时定义了构造方法,系统将不会自动添加默认的构造方法。
在一个类中定义构造方法,必须满足以下3个条件:
(1)方法名与类名相同;
(2)在方法名的前面没有返回值类型的声明;
(3)在方法中不能使用return 语句返回一个值。
代码实例
3.6 访问修饰符与static关键字
3.6.1 访问修饰符
C#中,4种访问修饰符,可以组合成5个可访问级别,访问级别从高到低描述如下:
(1)public:公有访问,*别访问,访问不受任何限制;
(2)protected:保护访问,只限于本类和子类访问,实例不能访问;
(3)internal:内部访问,只限于本项目中的类或子类访问,其它不能访问;
(4)protected internal:内部保护访问,只限于本项目中的类或子类访问,其它不能访问;
(5)private:私有访问,最低访问级别,只限于在声明它们的类和结构中才可以访问,子类和实例都不能访问。
在使用访问修饰符定义命名空间、结构和类及其成员时,要注意以下几点:
(1)一个成员或类型只能有一个访问修饰符,使用protected internal组合时除外;
(2)命名空间上不允许使用访问修饰符,命名空间没有访问限制;
(3)如果未指定访问修饰符,则使用默认的可访问性,类的成员默认为private;
(4)访问修饰符只是控制外部对内部成员的访问,类的内部对自己的访问不受其限制,即在类的内部可以访问所有的类成员。
3.6.2 static关键字
static关键字用于修饰类、字段、属性、方法以及构造方法等。被static修饰的成员称为静态成员,包括静态字段、静态属性、静态方法等;被static修饰的类称为静态类。
静态成员与非静态成员的不同在于:静态成员属于类,而不属于类的实例,因此,需要通过类而不通过类的实例访问;而非静态成员总是与特定的实例(对象)相联系。
在实际应用中,当类的成员引用或操作的信息是属于类的实例时,就应该设置为静态成员。???
1静态字段
静态成员属于类,而不属于类的实例,因此,需要通过类而不通过类的实例访问;而非静态成员总是与特定的实例(对象)相联系。
语法格式:类名.静态字段名
2静态属性
静态属性可以读写静态字段的值,并保证静态字段值的合法性,访问静态属性的语法格式如下:
类名.静态属性
3静态方法
当希望不创建对象就可以访问某个方法时,可以将该方法定义成静态方法,访问静态方法的语法格式如下:
类名.静态方法名
4静态类
当类的成员全部是静态成员时,就可以把这个类声明为静态类。静态类具有的有点:编译器能够自动执行检查,以确保不添加实例成员;静态类能够使程序的实现更简单、迅速,因为不必创建对象就能调用其方法。
静态类特点:
(1)静态类仅包含静态成员
(2)静态类不能被实例化
(3)静态类是密封的
(4)静态类不能包含实例构造方法
5综合代码实例
3.7 面向对象的基本特征
面向对象的3个基本特征是:封装、继承、多态
封装是指 将客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或对象操作,对不可信的类进行信息隐藏。通常,一个类就是一个封装了数据以及操作这些数据的代码逻辑实体。
继承是指 可以让某个类型的对象获得另一个类型对象的属性和方法。通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”“父类”或“超类”。继承的过程,就是从一般到特殊的过程。
多态是指 一个类的实例的相同方法在不同情形有不同的表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,这些操作可以采用相同的方式予以调用。
3.7.1 封装
C#中可以使用类达到数据封装的效果,这样就可以使数据与方法封装成单一元素,以便通过方法存取数据。
3.7.2 继承
C#中提供了类的继承机制,但只支持单继承,而不支持多重继承。利用继承机制,可以通过增加、修改或替换类中的方法对这个类进行扩充,以适应不同的应用需求。继承使子类可以从父类自动地获得父类所具备的特性,故可以极大地节省代码,提高代码的可重用性。
继承的语法格式如下:
[访问修饰符] class 类名:基类名
{
类成员;
}
注意:
在C#中,类的继承遵循以下原则:
(1)派生类只能从一个类中继承,即单继承。
(2)派生类自然继承基类的成员,但不能继承基类的构造方法。
(3)类的继承可以传递。若类C继承于类B,类B又继承于类A,那么类C具有类A和类B的成员。
3.7.3 多态
在C#中,类的多态性是通过在子类(派生类)中重写基类的虚方法或方法成员实现的。若一个方法在定义中含有virtual修饰符,则该方法称为虚方法。虚方法的实现可以由派生类取代,取代所有继承的虚方法的实现过程称为重写(override)。
3.8 抽象类与嵌套类
抽象类和嵌套类的概念是面向对象设计中常用的概念。抽象类往往用来表示对问题领域进行分析、设计中得出的抽象概念,是对一系列看上去不同,但是本质上相同的具体概念的抽象。嵌套类的目的在于隐藏类名,减少全局的标识符,从而限制用户使用该类建立对象,这样可以提高类的抽象能力,并且强调了两个类(外部类和嵌套类)之间的主从关系。
3.8.1 抽象类
基类并不与具体的事物联系,而只是表达一种抽象的概念时,可以将此基类定义为抽象类,用于为它的派生类提供一个公共的界面。
抽象类是类,可以包含具体的功能实现代码。抽象类以abstract修饰;不能被实例化,只能供其它类继承;含有抽象方法的类一定要声明为抽象类,但反过来抽象类可以不包含抽象方法。
注意:
(1)抽象类只能作为其它类的基类,不能直接被实例化,而且对抽象类不能使用new操作符;
(2)包含抽象类方法的类必须声明为抽象类,但抽象类可以不包含抽象方法;
(3)如果一个非抽象类从抽象类中派生,则其必须通过重写实现所有继承而来的抽象成员。
抽象类与接口的联系与区别:
参考:
https://www.cnblogs.com/jian1125/p/10530125.html https://www.cnblogs.com/sunzhenyong/p/3814910.html
https://blog.csdn.net/xiangrublog/article/details/20392781
3.8.2 嵌套类
在C#中,可以将类定义在另一个类的内部,被包含的类称为嵌套类,而包含嵌套类的类称为外部类。
3.9 委托与Lambda表达式
3.9.1 委托
委托是一种动态调用方法的类型。在C#程序中,可以声明委托类型、创建委托的实例、把方法封装于委托对象中,这样就可以通过该对象调用方法了。
委托对象本质上代表了方法的引用。在 .NET Framework中,委托有以下特点:
- 委托类似于C++的函数指针,但与函数指针不同的是,委托是完全面向对象的,是安全的数据类型;
- 委托允许将方法作为参数进行传递;
- 委托可用于定义回调方法;
- 委托可以把多个方法链接在一起,这样在事件触发时,可以同时启动多个事件处理程序。
1 声明委托
委托使用关键字delegate来声明,语法格式:
[访问修饰符]delegate 返回值类型 委托名 ([形参列表])
其中,访问修饰符可选,返回值类型和委托名是必要的,形参列表用来指定委托所表示方法的参数,也是可选的。
例如:public delegate int Calculate(int x, int y);
2 委托实例化
委托对象必须用new关键字创建,且与一个特定的方法有关。当创建委托时,传递到new语句的参数就像方法调用一样书写,但是不带有参数。语法格式:
委托类型 委托变量名 = new 委托类型构造方法 (委托要引用的方法名)
3 使用委托
在实例化委托之后,就可以通过委托对象调用它所引用的方法。在使用委托对象调用所引用的方法时,必须保证参数的类型、个数、顺序和方法声明匹配。
4 代码示例
3.9.2 Lambda表达式
Lambda表达式本质上就是匿名方法,只是将匿名方法的书写方式进一步简化。由于方法需要依附于委托,所以Lambda表达式的书写也要遵循委托的限制。语法格式:
(参数列表)=>{语句序列};
注意:
- 参数列表中可以有0个,1个或更多参数,参数个数由相应的委托确定。
(2)当参数列表中只有一个参数时,参数列表外侧的一对括号可以省略。
(3)当编译器能够推断参数的类型时,在参数列表中可以不必确定参数类型,只需要参数的名称即可。
(4)如果在委托声明时对参数使用了ref或out修饰,则Lambda表达式中也必须带上ref或out,并且此时不能省略参数类型。
(5)当右侧的语句序列只有一条语句时,大括号可以省略,否则不能省略。
(6) 当右侧的语句序列有返回值,必须使用return语句,但是若右侧语句序列中只有一个语句,则return语句可以省略。
(7)如果委托有返回值类型,则Lambda表达式也必须返回相同类型的值。
代码示例
3.10 程序集
在程序开发时可能会用到其它程序中的类,此时就需要使用程序集。程序集就是包含一个或多个类型的定义文件和资源文件的集合,该程序集中的文件可以被其它程序使用。
1生成程序集
1)创建类库
2)编写类库中的类
3)生成程序集
2引用程序集
新建项目ConsoleApplication,右击项目,选择“添加”->“引用”命令,打开“引用管理器”对话框,将相应.dll文件添加进去即可。
3使用程序集中的类
可参考:
https://blog.csdn.net/u010026134/article/details/51598499