C#学习笔记——(六)
一、面向对象的编程
- 面向对象的编程帮助程序员通过建立真实世界的问题模型来管理复杂事物。
1、类和对象
- 类定义了一个新类型,并且用于代表问题域(要解决的问题和所了解的于问题相关的全部信息)中的一类事物;
- 对象是类的实例;
- 状态是对象的当前状态;
- 许多类定义了成员域,这些成员域是能够被类中每个方法所见的私有变量,但不能被类以外的方法所见;
- 类的行为通过方法来定义,方法中包含执行动作的代码。方法能够操作对象的状态,并能够于其他对象交互。
2、面向对象编程的三大支柱
- 面向对象编程的三大支柱是封装、具体化和多样性。
1、封装要求每个类都是独立的类,每个类应该"知道"或者"做"一件独立的事情或者一系列的事;
2、具体化通过广义类(基类)派生出许多具体类来实现。具体化的类继承了一般化类的许多特性,具体化的类叫作派生类,一般化的类被认为是基类;
3、多样性允许将不同的类作为一个集合,这些类派生出一个公共类,并且处理这些类与处理基类类型实例相同
二、类和对象
1、什么是类什么是对象
1>类和对象
- 面向对象:一种软件开发的思想,指导程序员如何分析、解决问题
- 类是一个对象的概念,即位生活中的"类别"
- 对象是类的具体实例,即归属于某个类别的"个体"
- 例如:学生是一个类,表示一种类型,"八戒同学"则是一个对象
- 名词类型的共性,作为数据成员
- 动词类型的共性,作为方法成员
2>创建类
- 语法:
访问级别 ~~ class ~~ 类名
{
~~~~~ 类成员…
} - 通常每个类都在一个独立的C#源文件中
- 创建新的类意味着在当前项目中产生了一种新的数据类型
- 类中的成员,如果不加访问修饰符,默认都是private
//Wife.cs
namespace Day07
{
/// <summary>
/// 定义 老婆 类
/// </summary>
class Wife
{
//数据成员
private string name;
private string sex;
private int age;
//方法成员
public void SetName(string name)
{
//this 这个对象(引用)
this.name = name;
}
public string GetName()
{
return name;
}
public void SetAge(int age)
{
this.age = age;
}
public int GetAge()
{
return this.age;
}
}
}
3>创建对象
- 语法:
类名 ~~ 引用名;
引用名 = new 构造函数(参数列表); - 创建对象的过程也被成为"实例化"
static void Main()
{
//声明Wife类型的引用
Wife wife01;
//指向Wife类型的对象(实例化Wife类型对象)
wife01 = new Wife();
wife01.SetName("令狐");
wife01.SetAge(18);
Wife wife02 = wife01;
wife02.SetName("小龙女");
//Wife wife02 = wife01;
//wife02 = new Wife();
//wife02.SetName("小龙女");
Console.WriteLine(wife01.GetName());
Console.WriteLine(wife01.GetAge());
}
2、构造函数
1>定义
- 提供了创建对象的方式,初始化类数据成员的特殊方法
- 语法:
[访问修饰符] ~~ 类名 ([参数列表])
{
~~~~ 初始化……
}
2>特点
- 与类同名
- 没有返回值,也不能写void
- 不能被直接调用,必须通过new运算符在创建对象时才会自动调用
- 每个类都必须至少有一个构造函数,若不提供,编译器会自动生成一个无参构造函数
- 如果程序员定义了构造函数,则编译器不会再提供无参构造函数
//构造函数:提供了创建对象的方式,常常用于初始化类的数据成员
//一个类若没有构造函数,那么编译器会自动提供一个无参数构造函数
//一个类若具有构造函数,那么编译器不会提供无参数构造函数
//本质:方法
//特殊:没有返回值 与类同名 创建对象时自动被调用(不能手动调用)
//如果不希望在类的外部被创建对象,就将构造函数私有化
//private Wife() { }
public Wife()
{
Console.WriteLine("创建对象时执行");
}
public Wife(string name):this()
{
//Wife();调用无参数构造函数
this.name = name;
}
public Wife(string name,int age):this(name)
{
//Wife(name);
//this.name = name;
//this.age = age;//构造函数如果为字段赋值,属性中的代码块不会执行
this.Age = age;
}
3>析构函数
-
语法
~类名()
{
~~~~ 语句块;
} -
注意:
1、只能在类中使用析构函数,不能在结构中使用析构函数
2、一个类只能有一个析构函数
3、不能继承或重载析构函数
4、析构函数只能被自动调用
5、析构函数没有任何修饰符、没有任何参数、也不返回任何值 -
调用析构函数
1、垃圾回收器决定了析构函数的调用,我们无法控制何时调用析构函数
2、垃圾回收器检查是否存在应用程序不在使用的对象。如果垃圾回收器认为某个对象符合析构,则调用析构函数(如果有)并回收用来存储此对象的内存
3、程序退出时会调用析构函数 -
作用:结合垃圾回收器使用,帮助释放资源
3、修饰符
1>访问修饰符
- C#共有五种访问修饰符:public、private、protected、internal、protected internal。(具体在封装中进行解释)
- 能够访问类的修饰符只有两个:public、internal(默认)
- C#成员类型的可修饰及默认修饰符如下表:
具体案例点击:传送门.
2>readonly修饰符
- 字段可以用readonly修饰符声明。 其作用类似于将字段声明为const,但主要有以下区别:
1、const字段只能在字段的声明语句中初始化,而readonly可以在以下任意位置设定它的值:
----字段声明语句
----类的任何构造函数
2、const字段的值必须可在编译时决定,而readonly字段的值可以在运行时决定(允许在不同的环境或构造函数中设置不同的值)
3、const的行为总是静态的,而对于readonly字段有以下两点是正确的:
----它可以是实例字段,也可以是静态字段
----它在内存中有存储位置
class Shape
{
readonly double PI = 3.1415;//静态字段 ,值在字段中声明
readonly int Number;//实例字段
public Shape(double numberOne ,double numberTwo)
{
Number = 2;//值在构造函数中声明
}
public Shape(double numberOne, double numberTwo,double numberThree)
{
Number = 3;//值在构造函数中声明
}
}
4、this关键字
- 访问当前类成员时,使用this关键字,可以提高代码的可读性;在没有歧义的情况下,也可以省略
- this关键字在类中使用,是对当前实例的引用,它只能被用在下列类成员的代码块中:
----实例构造函数
----实例方法
----属性和索引器的实例访问器 - 使用this关键字的目的
1、用于区分类的成员和局部变量或参数
2、作为调用方法的实参
5、属性
1>属性
- 对字段起到保护作用,对字段的取值和设置进行限定,可实现只读、只写功能
- 本质就是对字段的读取与写入方法
- 语法:
[访问修饰符] ~~ 数据类型 ~~ 属性名
{
~~~~ get { return 字段;}
~~~~ set {字段 = value;}
} - 通常一个公有属性和一个私有字段对应
- 属性只是外壳,实际上操作的私有字段
//字段首字母小写,属性首字母大写
//数据成员
//字段:存储数据 老板
private string name;
private string sex;
private int age;
//属性:保护字段 本质就是2个方法 助理
public string Name
{
//读取时保护
get
{
return name; }
//写入时保护 value 要设置的数据
set
{
this.name = value; }
}
public int Age
{
get
{
return age; }
set
{
if(value<=19 && value>=18)
this.age = value;
}
}
2>自动属性
- 当属性访问器中不需要任何其他逻辑时,使用自动属性可以更加简洁
- 本质就是一个字段两个方法
- 语法:
[访问修饰符] ~~ 数据类型 ~~ 属性名 {get;set;} - 例如:
public ~~ int ~~ Id {get;set;}
6、索引器
-
属性是一对get和set访问器,而索引器是一组get和set访问器
-
索引器与属性在很多方面是相似的:
1、和属性一样,索引器不用分配内存来存储
2、索引器和属性都主要用来访问其他数据成员,它们与这些成员关联,并未他们提供获取和设置访问
----属性通常表示单个数据成员;
----索引器通常表示多个数据成员; -
语法:
返回值类型 ~ this ~ [参数1,参数2…]
{
~~~ get
~~~ {
~~~~~~~ …
~~~ }
~~~ set
~~~ {
~~~~~~~ …
~~~ }
}
注意:
1、索引器没有名称。在名称的位置是关键字this
2、参数列表在方括号中间
3、参数列表中必须至少声明一个参数 -
索引器也可以进行重载,因为所有的索引器具有相同的"名称":this访问引用
class Employee
{
public string LastName;
public string FirstName;
public string CityOfBirth;
//索引器
public string this[int index]
{
get
{
switch(index)
{
case 0:
return LastName;
case 1:
return FirstName;
case 2:
return CityOfBirth;
default://一定要加上,保证在不能获得正确值的时候不会出错
throw new ArgumentOutOfRangeException("index");
}
}
set
{
switch (index )
{
case 0:
LastName = value;
break;
case 1:
FirstName = value;
break;
case 2:
CityOfBirth = value;
break;
default://此处可不加
throw new ArgumentOutOfRangeException("index");
}
}
}
}
7、类结构
- 访问级别 ~~ class ~~ 类名
{
~~~~ 字段:存储数据
~~~~ 属性:保护字段
~~~~ 构造函数:提供创建对象的方式,初始化类的数据成员
~~~~ 方法:向类的外部提供某种功能
}
1>分部类和分布类型
-
类的声明可以分割成几个分布类的声明:
1、每个分布类的声明都含有一些类成员的声明
2、类的分布类声明可以在同一个文件中,也可以在不同的文件中 -
注意:
1、每个分部类声明必须被标注为partial class,而不是单独的关键字class
2、类型修饰符partial不是关键字,所以在其他上下文中,可以在程序中把它用作标识符。但直接用在关键字class、struct或interface之前时,它表示分布类型。
2>分部方法
- 分布方法是声明在分布类中不同部分的方法,分布方法有两个部分:
1、定义分部方法声明
----给出签名和返回类型
----声明的实现部分只是一个分号
2、实现分部方法声明
----给出签名和返回类型
----以普通的语句块形式实现 - 注意:定义声明和实现声明的签名和返回值必须匹配,签名和返回类型具有如下特征:
1、返回类型必须是void
2、签名不能包含访问修饰符,这使分部方法是隐式私有的
3、参数列表不能包含out参数
4、在定义声明和实现声明中必须包含上下文关键字partial,并且直接放在关键字void之前
partial class MyClass
{
partial void PrintSum(int x, int y);//定义分部方法,无方法体
public void Add(int x,int y)
{
PrintSum(x, y);
}
}
partial class MyClass
{
partial void PrintSum(int x, int y)//实现分部方法
{
Console.WriteLine("Sum is {0}",x+y);
}
}
class Program
{
static void Main(string[] args)
{
var mc = new MyClass();
mc.Add(5, 6);
}
}
8、Static
1>静态成员变量
- 使用static关键字修饰的成员变量
- 静态成员变量属于类,类被加载时初始化,且只有一份
- 实例成员变量属于对象,在每个对象被创建时初始化,每个对象一份
- 特点:存在优先于对象(比对象存在更早),被所有对象所共享,常驻内存
2>静态构造函数
- 初始化类的静态数据成员
- 仅在类被加载时执行一次
- 不允许使用访问修饰符
3>静态方法
- 通过引用调用实例方法时,会隐式的传递对象引用,以便在方法内部可以正确访问该对象成员变量
- 通过类名调用静态方法时,因为没有具体对象,所以在static方法中不能访问实例成员
- 从时间上理解:
静态的先于实例的,因此后面的可以调用前面的,但是前面的不能调用后面的
4>静态类
- 使用static关键字修饰的类
- 不能实例化,只能包含静态成员
- 静态类不能被继承,但是静态方法、属性都可以被继承
/// <summary>
/// 学生类
/// </summary>
class Student:People
{
//实例(对象)成员 静态成员
//
//prop + tab + tab
/// <summary>
/// 分数
/// </summary>
public int Score {
get; set; }
//每个对象都存储一份 "杯子"
public int InstanceCount;
//仅仅存储一份 所有对象共享 "饮水机"
public static int StaticCount;
//实例构造函数作用:提供创建对象的方式,初始化类的实例数据成员
public Student()
{
InstanceCount++;
StaticCount++;
}
//静态构造函数作用:初始化类的静态数据成员
//执行时机:类加载时调用一次
static Student()
{
//"非静态字段 要求 对象引用 / 对象引用对于非静态是必须的" ==> 静态代码块,只能访问静态成员
//InstanceCount++;
StaticCount++;
}
public static void Fun1()
{
//Console.WriteLine(InstanceCount);
}
public void Fun2()
{
Console.WriteLine(this.InstanceCount);
}
}
5>适用性
- 利:单独空间存储,所有对象共享,可直接被类名调用
弊:静态方法中只能访问静态成员,共享数据被多个对象访问会出现并发 - 适用场合
1、所有对象需要共享的数据
2、在没有对象前就要访问成员
3、工具类适合做静态类(常用,不需要过多数据)