C#OOP面向对象编程
一、深入.NET框架
1.1 什么是.NET
微软公司作为全球最大的电脑软件提供商,为了占据开发者市场,进而在2002年推出了Visual Studio(简称VS,是微软提供给开发者的工具集) .NET 1.0版本的开发者平台。而为了吸引更多的开发者涌入平台,微软还在2002年宣布推出一个特性强大并且与.NET平台无缝集成的编程语言,即C# 1.0正式版的出现。而C#这门语言就是为宣传.NET而创立的,它直接集成于Visual Studio .NET中。
1.2 .NET框架
.NET框架是基于Windows操作系统上的.NET最基础的框架。它提供了创建、部署、运行.NET应用的环境,主要包括公共语言进行时(CLR)和框架类库(.NET Framework 类库,FCL)并且支持多种开发语言。
1.2.1 CLR
CLR全称是公共语言进行时(Common Language Runtime),它是所有.NET程序应用运行和开发的环境。
1. .NET编译技术
为了实现跨语言语言和平台的开发的目标,.NET所有编写的应用都不编译成本地代码,而是转换成微软中间代码(Microsoft Intermediate Language MSIL)。它由JIT(Just In Time)转换成机器代码。
2. CTS
CTS(通用类型系统)用于解决不同语言数据类型不同的问题,它把数据类型编译成通用的。
3.CLS
在不同的编程语言中语法也都有很大区别,所有.NET通过定义公共语言规范(CLS)的限制解决。
1.2.2FCL
在.NET框架中还有另外一个重要的部分是FCl,也就是框架类库。在使用FCL时,我们会引入一些相应的命名空间。
二、深入C#数据类型
2.1 值类型和引用类型
2.1.1 基本数据类型
数值 | int,long,double,float |
---|---|
字符 | char |
布尔 | bool |
枚举 | enum |
结构 | struct |
基本数据类型的储存空间是在栈里面,命名空间在System.ValueType。
2.1.2 引用数据类型
类 | class、string |
---|---|
数组 | 数据类型[] |
集合 | List<> |
基本数据类型的储存空间是在堆里面,命名空间在System.Object。
2.1.3 结构
1、结构定义
语法:
访问修饰符 struct 结构名
{
//结构体
}
类似类,但struct是值类型。
注意:
1、字段不能赋初始值。
2、结构中可以有字段也可以有方法。
2、结构使用
1、可以new也可以不用new。
2、不new不能直接给属性赋值。
2.2 参数传递
2.2.1 值传递
1、基本数据类型:形参所做改变不会影响实参。
2、引用数据类型:形参所做改变会影响实参(同一个堆里面,string除外)。
2.2.2 引用传递
1.形参所做修改都会影响实参。
2.形参和实参都要在前面加ref关键字。
2.2.3 装箱与拆箱
在编程中,值类型(基本数据类型)转换为引用类型(引用数据类型)的过程称为装箱。引用类型转换为值类型的过程称为装箱。在开发中应当尽量避免装箱与拆箱,减少性能损失。
三、使用集合组织相关信息
3.1 集合概述
3.1.1 集合定义
1、保存一组大小不固定的值。
2、泛型跟非泛型:
泛型:限定了数据类型。
非泛型:没有限定数类型。
3.1.2 类似数组的集合
1、ArrayList(非泛型)
语法:
ArrayList 对象名 = new ArrayList();
方法与属性:
属性 | 使用 | 说明 |
---|---|---|
Count | 对象名.Count; | 得到集合内元素个数 |
方法 | 使用 | 说明 |
Add(Object obj); | Add(数据类型); | 添加在集合中,可以存放任意数据类型 |
Remove(Object obj) | Remove(值) | 移除在集合中指定的值 |
RemoveAt(int index) | RemoveAt(下标) | 移除在集合中指定下标的值 |
Clear() | 对象名.Clear(); | 清除集合中所有元素 |
遍历:
for:借助下标。
for(int i = 0; i < ArrayList.Count; i++)
{
cw(ArrayList[i]);
}
foreach:借助中间变量。
foreach(Object obj in ArrayList)
{
cw(obj);
}
2、List(泛型)
语法:
List<T> 对象名 = new List<T>();
T在这里代表任意一种数据类型。
方法与属性:
属性 | 使用 | 说明 |
---|---|---|
Count | 对象名.Count; | 得到集合内元素个数 |
方法 | 使用 | 说明 |
Add(Object obj); | Add(数据类型); | 添加在集合中,可以存放定义的类型。 |
Remove(Object obj) | Remove(值) | 移除在集合中指定的值 |
RemoveAt(int index) | RemoveAt(下标) | 移除在集合中指定下标的值 |
Clear() | 对象名.Clear(); | 清除集合中所有元素 |
遍历:
for:借助下标。
for(int i = 0; i < list.Count; i++)
{
cw(list[i]);
}
foreach:借助中间变量。
foreach(T item in list)
{
cw(item);
}
3、ArrayList和List的区别
相同:
1、相同的属性:Count。
2、相同的方法:Add,Remvoe,RemoveAt,Clear。
3、相同的遍历方式。
不同点:
1、ArrayList添加数据的时候不限定值的类型list限定了添加的值的类型。
2、ArrayList在取值的时候,涉及到了类型转换List没有,只有一种数据类型。
3.1.3 以key:value存取的集合
“key:value”中key代表一个键,value代表一个具体的值。
1、Hashtable(非泛型)
语法:
Hashtable 对象名 = new Hashtable();
方法与属性:
属性 | 使用 | 说明 |
---|---|---|
Count | 对象名.Count; | 得到集合内键/值对个数 |
Keys | 对象名.Keys; | 得到集合内键的集合 |
Values | 对象名.Values; | 得到集合内值的集合 |
方法 | 使用 | 说明 |
Add(Object key,Object value); | Add(数据类型); | 将指定键和值的元素添加在集合中,可以存放任意数据类型。 |
Remove(Object obj) | Remove(值) | 移除在集合中指定键的值 |
Clear() | 对象名.Clear(); | 清除集合中所有元素 |
遍历:
1、遍历所有key和value
foreach(Object key in ht.Keys)
{
cw(key + ht[key]);
}
ht[key]区得的值。
2、遍历所有的value
foreach(Object value in ht.Values)
{
cw(value);
}
2、Dictionary(泛型)
语法:
Dictionary<TKey,Tvalue> dic = new Dictionary<TKey,Tvalue>();
方法与属性:
属性 | 使用 | 说明 |
---|---|---|
Count | 对象名.Count; | 得到集合内键/值对个数 |
Keys | 对象名.Keys; | 得到集合内键的集合 |
Values | 对象名.Values; | 得到集合内值的集合 |
方法 | 使用 | 说明 |
Add(Object key,Object value); | Add(数据类型); | 将指定键和值的元素添加在集合中,可以存放任意数据类型。 |
Remove(Object obj) | Remove(值) | 移除在集合中指定键的值 |
Clear() | 对象名.Clear(); | 清除集合中所有元素 |
遍历:
1、遍历所有key
foreach(Object key in ht.Keys)
{
cw(key + ht[key]);
}
2、遍历所有的value
foreach(Object value in ht.Values)
{
cw(value);
}
3、Hashtable和Dictionary区别
相同:
1、相同的属性:Count。
2、相同的方法:Add,Remvoe,Clear。
3、相同的遍历方式。
不同点:
1、hashtable没有限定类型
2、Dictionary限定了key和value的数据类型
3.1.4 非泛型和泛型的区别
泛型:限制了数据类型。
非泛型:没有限制数据类型。
四、深入类的方法
4.1 构造函数
4.1.1 定义
语法:
访问修饰符 类名(){}
注意:
1、类似方法,只是少了一个返回值
2、方法名有要求:跟类名一致
3、构造函数也是会被重载
构造函数就是用来new对象,另外兼职给成员变量赋值。
4.1.2 分类
显示方式:
隐式:隐藏的无参
创建一个类,没有手写构造函数的时候当前类自动生成的构造函数。
显示:
自定义构造就是自定义构造函数
参数的格式:有参;无参。
4.1.3 特点
只要定义一个类,如果没有手写构造函数,那么这个类会自带一个无参的构造函数,并且这个构造函数比隐藏只要你手写了构造函数,那么这个类就不会存在隐藏构造函数(不会有默认的无参构造函数)。
4.2 方法重载
4.2.1 定义
1、在同一个类中。
2、相同的方法名。
3、不同的参数:
1、个数不相同。
2、对应的参数下标的参数类型不同。
4.2.2 存在意义
为了解决方法名命名太多的问题。
4.2.3 调用规则
1、匹配相同的参数个数。
2、匹配对应下标的参数的类型。
注意:
1、不能以返回值类型来决定是否是重载。
2、能以访问修饰符来决定是否是重载。
五、继承与多态
5.1 继承:子承父
5.1.1 概念
继承是描述2个类之间的关系。
子类/派生类:继承的类。
父类/基类:被继承的类。
一旦产生继承关系,父类里面的非private修饰的所有成员都会变成子类成员的一部分。
语法:
class 子类名 : 父类名{}
5.1.2 子类与父类间的调用
1、成员的使用权限
访问修饰符是制定访问范围的。2个类之间产生了继承关系,子类里面可以直接使用父类里面的非private成员,但只有public成员在本类以外new对象可以直接调用。所有的类中只有只有public可以直接调用是new对象可以直接调用的。
2、构造函数的使用
1、每一个子类的构造函数的使用一定会调用父类的构造函数。
2、默认的情况下调用的是父类的无参构造函数(隐式)。也可以显示调用无参构造函数:
public 子类名():base(){}
“:base()”是在调用父类的无参构造函数。
“:base(值)”表示在调用父类的有参构造函数,要注意“()”内放的是实参,也就是实际的值。
3、多个子类的调用判断
1、is a:判断子类真正的数据类型。
语法:
对象名 is 数据类型;
在判断之后还需要强转数据类型。
2、 as a:强转
数据类型 变量 = 对象名 as 数据类型;
数据类型 变量 = (数据类型)对象名;
这两种方法是等价的,需要注意的是如果不判断子类真正的数据类型就强转运行过程中会报错。
5.1.3 继承的特性
1、单根性:
每一个类都只能有一个父类,没有例外。
2、传递性:
从父类继承到的所有成员都可以传递给子类的子类。
注意:
每一个类如果没有显示定义父类,都有一个默认父类:Object。
如果某一个类想要禁止被继承可以在class前面使用关键字:sealed。
5.2多态
5.2.1 定义
一个事务的多种形态,也可以说是一个类的多种形态。
5.2.2 作用
在上文中谈到多个子类的调用是需要判断的,如果数量越多判断也就越多,而多态可以解决这个问题。
5.2.3 多态的实现方式
1、重载
重载用于同一个类多态的实现,让一个类相同的方法在不同的参数下有不同的表现。
2、重写
重写用于不同类的多态的实现,前提是两个类必须存在继承关系。
定义:
1、不同的类中。
2、相同的方法名。
3、相同参数。
重写的实现方式:
1、虚方法:virtual+override
语法:
父类
public class 父类名
{
public virtual void 方法名()
{
//方法体
}
}
子类
public class 子类名 : 父类名
{
public override void 方法名()
{
//方法体
}
}
注意:
- 父类中必须有方法体。
- 子类可以不重写虚方法。
2、抽象方法:abstract+override
语法:
父类
public abstract class 父类名
{
public abstract void 方法名();
}
子类
public class 子类名 : 父类名
{
public override void 方法名()
{
//方法体
}
}
注意:
- 只有“abstract”关键字修饰的抽象类才能有抽象方法。
- 抽象类不能实例化。
- 抽象类可以有抽象方法和非抽象方法。
- 父类的抽象方法子类必须实现。
- 抽象类不能以“sealed”和“static”修饰。
5.3 软件设计原则
软件设计开发的过程中总有一些经验积累的组合,这就是软件设计原则。这里我们就先了解一下其中的一条。
里氏替换原则(LSP):所有父类出现的地方都必须能用子类对象。
在满足了里氏替换原则的情况下,继承复用才成为可能,程序才能继续不断的拓展下去。
六、总结
- 父类中必须有方法体。
- 子类可以不重写虚方法。
2、抽象方法:abstract+override
语法:
父类
public abstract class 父类名
{
public abstract void 方法名();
}
子类
public class 子类名 : 父类名
{
public override void 方法名()
{
//方法体
}
}
注意:
- 只有“abstract”关键字修饰的抽象类才能有抽象方法。
- 抽象类不能实例化。
- 抽象类可以有抽象方法和非抽象方法。
- 父类的抽象方法子类必须实现。
- 抽象类不能以“sealed”和“static”修饰。
5.3 软件设计原则
软件设计开发的过程中总有一些经验积累的组合,这就是软件设计原则。这里我们就先了解一下其中的一条。
里氏替换原则(LSP):所有父类出现的地方都必须能用子类对象。
在满足了里氏替换原则的情况下,继承复用才成为可能,程序才能继续不断的拓展下去。
六、总结
学习编程不仅仅是只学习语法,最重要的是去学习其中的思想。为什么这样编程不行。现在作为一个学习者是看不到其中的问题,所以在逐步学习的过程中应当多了解先行者总结的规则,并坚实一步一步走下去。