深入了解C#

深入了解C#

.NET框架

什么是.NET框架

  1. 它是开发.NET应用程序的核心基础
  2. 它提供了一个面向对象的编程环境,完全支持面向对象编程
  3. 对Web应用的强大支持
  4. 对Web Service(Web服务)的支持
  5. 实现SOA,支持云计算
  6. 支持构建.NET程序的炫彩外衣

.NET框架体系结构

.NET框架运行在操作系统之上,是.NET最基础的框架,它提供了创建、部署和运行.NET应用的环境,主要包含公共语言运行时(CLR)和框架类库(.NET Framework类库,FCL),并且支持多种开发语言。

CLR

CLR的全称是公共语言运行时(Common Language Runtime),它是所有.NET应用程序运行时环境,是所有.NET应用程序都要使用的编程基础。CLR包含两个组成部分:CLS(公共语言规范)和CTS(通用类型系统)。

CTS

CTS通用类型系统(Common Type System)用于解决不同语言数据类型不同的问题。

CLS

CLS公共语言规范(Common Language Specification)限制了由这些不同点引发的互操作性问题,是一种最低的语言标准。

FCL

FCL是.NET框架的一个重要部分,即框架类库。在.NET框架中提供了非常丰富实用的类库,这些类库都是我们进行软件开发的利器例如

System; //此命名空间包含所有其他的命名空间
System.Collections.Generic;//支持泛型操作
System.IO;//支持对文件的操作
System.Net;//支持对网络协议的编程
System.Data;//提供对表示ADO.NET结构的类的访问
System.Windows.Forms;//用于开发Windows应用程序
System.Drawing;//支持GDI+基本图形操作

以上都是一些.NET框架的核心类库及其2功能。

面向对象回顾

类和对象

我们都知道关于类和对象之间的关系:

  1. 对象是类的具体化;
  2. 类是对象的抽象化

对于类的属性,我们通过get和set访问器进行访问和设置,用来保障类中数据的安全,如:

  1. 只写属性:只包含set访问器;
  2. 只读属性:只包含get访问器;
  3. 读写属性:同时包含set访问器和get访问器

封装

封装被称为信息隐藏,是指利用抽象数据类型将数据和数据的操作结合在一起,使其构成一个不可分割的独立实体,尽可能隐藏内部的细节,只保留一些对外接口,使之与外部发生联系。

封装主要给我们带来了如下好处:

  1. 保证数据的安全性
  2. 提供清晰的对外接口
  3. 类内部实现可以任意修改,不影响其他类

将字段封装为属性是封装的一种方式,类的私有方法也是一种封装。

C#数据类型

常用的数据类型:

常用数据类型 Java C#
整型 int int
浮点型 float float
双精度型 double double
字符串 String string
布尔型 boolean bool
枚举型 menu menu

值类型

值类型源于System.ValueType家族,每个值类型的对象都有一个独立的内存区域用于保存自己的值,值类型数据所在的内存区域称为栈(Stack)。

引用类型

引用类型源于System.Object家族,在C#中引用类型主要包括数组、类和接口等。

注意:值类型的值保存在栈中,一旦在代码中修改它的值,它保存在栈中的值都会发生改变。引用类型的值保存在堆中,同时会在栈中保存堆的地址,是指向性的,在修改同一个堆中的值时,才会改变。

结构

当发现一个类中具有的字段都是值类型的,而且个数比较少,此时我们可以用C#为我们提供的结构(Structure)这个数据类型。

结构的定义:

访问修饰符 struct 结构名
{
    //结构体
}

注意:结构中可以有字段,也可以有方法。定义时,结构中的字段不能被赋初值。不能手写构造函数,定义构造函数时有多少字段就要有多少个参数,为字段赋值。

关于结构的使用:

注意

  1. 可以不用new,直接定义结构的对象即可
  2. 声明结构的对象后,必须给结构的成员赋初值
  3. 不new的时候不可以直接调用属性赋值,会报错

装箱和拆箱

装箱:将值类型转换为引用类型的过程

拆箱:将引用类型转换为值类型的过程

注意:在拆箱时,被转换引用类型的数值必须能够转换为目标值类型。其次在实际的开发中,应该尽量减少不必要的装箱和拆箱,因为二者的存储方式不同,转换时性能损失较大。

参数传递

在C#中传递分为:1.值传递 2.引用传递

值传递

  1. 当使用引用类型作为参数时,当参数被修改时,值也会被修改(String除外)。
  2. 当使用值类型作为参数时,当参数被修改时,值不会被修改。

引用传递

  1. 在使用引用传递时,需用ref修饰。
  2. 当使用引用传递时,不管是值类型或引用类型时,参数所作修改,值都会被修改。

使用集合

数组是一组具有相同类型的数据的集合,但是数组的大小是固定的。从此我们可以想到是否能建立一个动态的“数组”,有了集合。

关于集合

ArrayList

ArrayList非常类似于数组,也有人称它为数组列表,ArrayList可以动态维护。

ArrayList类属于System.Collections命名空间,这个命名空间包含接口和类,这些接口和类定义各种对象的集合。

注意:ArrayList是动态可维护的,因此定义时即可以指定容量,也可以不指定容量。

定义:

using System.Collections;//使用前必须导入System.Collections命名空间
//创建容量为0的ArrayList对象
ArrayList students = new ArrayList();
//创建容量为5的ArrayList对象
ArryaList students = new ArrayList(5);

ArrayList的常用方法及属性

属性名称 说明
Count 获取ArrayList中实际包含的元素数
方法名称 说明
int Add(Object value) 将对象添加到ArrayList的结尾处
void RemoveAt(int index) 移除ArrayList指定索引处的元素
void Remove(Object value) 从ArrayList中移除特定对象
void Clear() 从ArrayList中移除所有元素

ArrayList获取一个元素的方法和数组是一样的,也是通过索引(index)来访问,ArrayList中的第一个元素索引是0。

注意:由于给ArrayList添加的元素都会被转换为Object型,所以在访问这些元素时必须把它们转化回原来的数据类型。

在遍历ArrayList中的元素,我们可以通过for循环和foreach循环,因为它是通过索引访问的。

//SE为自定义类
//for循环遍历
for(int i = 0; i < engineers.Count; i++)
{
    SE seFor = (SE)engineers[i];
    Console.WriteLine(seFor.Name);
}
//foreach循环遍历
foreach(Object obj in engineers)
{
    SE seForeach = (SE)obj;
    Console.WriteLine(seForeach.Name);
}

删除元素:ArrayList在添加和删除元素时都会使剩余元素的索引自动改变,Remove()方法和RemoverAt()方法只能删除一个元素。Clear()方法可以删除集合中的所有元素。

Hashtable

C#中提供了一种称为Hashtable的数据结构,通常称为哈希表,也有人称它为“字典”。Hashtable的数据是通过键(Key)和值(Value)来组织的。

Hashtable也属于System.Collections命名空间,它的每个元素都是一个键/值对。

Hashtable的常用方法及属性

属性名称 说明
Count 获取包含在Hashtable中键/值对的数目
Keys 获取包含在Hasbtable中的键的集合
Values 获取包含在Hashtable中值的集合
方法名称 说明
void Add(Object key,Object value) 将带有指定键和值的元素添加到Hashtable中
void Remove(Object key) 从Hashtable中移除带有特定键的元素
void Clear() 从Hashtable中移除所有元素

注意:Hashtable的Add()方法有两个参数,一个表示键,一个表示键所对应的值。

获取Hashtable的元素和ArrList不同,访问Hashtable元素时可以直接通过键名来获取具体值。同样,由于值的类型是Object,所有得到一个值是也需要通过类型转换得到指定类型的对象。

删除Hashtable的元素通过键(Key),使用Remove()方法删除。

遍历Hashtable的元素,因为Hashtable不能用索引访问,所以遍历Hashtable只能用foreach()方法。

Hashtable a = new Hashtable();
//遍历key
foreach(Object obj in engineers.Keys)
{
    Console.WriteLine((string)obj);
}
//遍历value
foreach(Object obj in engineers.Values)
{
    SE se = (SE)obj;
    Console.WriteLine(se.Name);
}

注意:以上代码是分别对engineers.Keys和engineers.Values遍历,而不是对engineers对象本身。

泛型和泛型集合

泛型是C#2.0中的一个新特性。泛型引入了一个概念:类型参数。通过使用类型参数(T)减少了运行时强制转换或装箱操作的风险,通过泛型可以最大限度地重用22代码、保护类型的安全及提高性能,它的最常见应用就是创建集合类,可以约束集合类中的元素类型。比较典型的泛型集合是List和Dictionary

List

定义:

List 对象名 = new List();

注意:“”中的T可以对集合中的元素类型进行约束,T表明集合中管理的元素类型。

List添加元素的方法和ArrayList是一样的,获取元素、删除元素,以及遍历一个List的方法和ArrayList的用法都是一样的,只是List保障了类型安全性,不需要类型转换。这里就不一一列举了。

List与ArrayList的区别

异同点 List ArrayList
不同点 对所保存元素进行类型约束 可以增加任何类型
添加/读取值类型元素无须拆箱、装箱 添加/读取值类型元素需要拆箱、装箱
相同点 通过索引访问集合中的元素
添加元素方法相同
删除元素方法相同

Dictionary

在C#中还有一种泛型集合Dictionary,它2具有泛型的全部特性,编译时检查类型约束,获取元素时无须类型转换,并且它存储数据的方式和Hashtable类似,也是通过Key/Value(键/值)对元素保存。定义:

Dictionary 对象名 = new Dictionary();

注意中的K表示集合中Key的类型,V表示Value的类型。他们的含有和List是相同的。

Dictionary的用法和Hashtable是一样的,注意数据类型,这里就不一一列举了。

Dictionary与Hashtable区别

异同点 Dictionary Hashtable
不同点 对所保存元素进行类型约束 可以增加任何类型
添加/读取值类型元素无须拆箱、装箱 添加/读取值类型元素需要拆箱、装箱
相同点 通过Key获取Value
添加元素方法相同
删除元素方法相同
遍历方法相同

泛型类

在集合中使用泛型只是泛型多种应用的一种,在类、方法等方面也有着泛型的广泛应用,使用泛型类,可以封装不是特定于具体数据类型的操作。

对于一些常常处理不同类型数据转换的类,可以使用泛型定义。

定义语法:

public class 类名
{
	//......
}

注意:T指类型参数,代表具体的数据类型,可以是类类型,也可以是基本数据类型。

泛型的优点:

  1. 性能高。
  2. 类型安全。
  3. 实现代码的重用。

类的方法

构造函数

构造函数是一种创建类实例的方法,是类中的一种特殊方法。

特点及作用:

  1. 方法名与类名相同。
  2. 没有返回值类型。
  3. 主要完成对象的初始化工作,兼职为成员变量赋值。

无参构造函数

在默认的情况下,系统将会给类分配一个无参构造函数,并且没有方法体,当然我们也可以自己定义一个无参方法。定义语法:

访问修饰符 类名()
{
	//方法体
}

有参构成函数

无参构造函数给成员变量赋值有一个缺点,就是属性值是固定的,为了满足对象多样化需求,我们就有了有参的构造函数。定义语法:

访问修改符 类名(参数列表)
{
 	//方法体   
}

参数列表一般用来给类的属性赋值。

注意:在类中,一旦自定义了构造函数,那么这个类的隐藏的默认无参构成函数就会消失,所以,我们一般都会自定义一个无参构造函数。

方法重载

多个构造函数提供了多种实例化一个类的方式,这种方式就是方法的重载;重载是多态的一种实现方式。在面向对象的语言中,允许我们在同一个类中定义多个方法名相同、参数列表(参数个数、参数类型)不同的方法,称为方法重载。

特点

  1. 方法名相同。
  2. 方法参数类型不同或者参数个数不同。
  3. 在同一个类中。

注意:方法名及参数列表相同的方法,仅是返回值类型不同,不能称为方法重载。

继承和多态

在之前我们也介绍过类的三大特性:封装、继承、多态,封装之前已经介绍过了,下面我们来介绍一下继承和多态。

继承

子承父:指一个类可以继承另一个类,子类拥有父类所有非 private 成员,还可以拥有自己独有的成员。在继承过程中,被继承的类被称为父类或基类,继承的类被称为子类或派生类,如:

public class Employee
{
	public Employe(){}
    //工号
    protected string ID { get; set; }
    //年龄
    protected int Age { get; set; }
    //姓名
    protected string Name { get; set; }
}
public class PM:Employee
{
	public PM(){}
	public int YearOfExperience { get => _yearOfExperience; set => _yearOfExperience = value; }//人气值
    public PM(string id, string name,int age,int yearOfExperience)
    {
        this.ID = id;
        this.Name = name;
        this.Age = age;
        this.YearOfExperience = yearOfExperience;
    }
}

注意:protected,受保护的,是一种访问修饰符,关于修饰符的区别如下:

修饰符 类内部 子类 其他类
public 可以 可以 可以
protected 可以 可以 不可以
private 可以 不可以 不可以

从表格中可以看出,三中访问修饰符对类成员的访问限制强度:private > protected > public。
注意:在类中 this 表是当前类的对象的引用,在子类中 base 表示调用父类的成员。

子类构造函数

当一个子类继承父类后,在调用子类构造函数时,会自动调用父类的构造函数,这种调用也分为隐式和显式调用。
隐式:在子类中没有指明要调用父类的哪一个构造函数,那么系统会隐式地调用父类的无参构造函数。
显式:C#中可以使用base关键字调用父类的构造函数,只要在子类的构造函数后添加":base(参数列表)",就可以指定该子类的构造函数调用父类的哪一个构造函数了。
注意:base关键字调用父类传递的参数列表是没有带数据类型的,只能传递参数。

继承的特性

继承的传递性:继承需要符合 is a 的关系,例如:“小型卡车 is a 卡车,卡车 is a 汽车,小型卡车 is a 汽车”,就是小型卡车继承卡车,卡车继承汽车,那么小型卡车继承汽车,这就是继承的传递性
继承的单根性:在C#中一个子类不能同时继承多个父类,只能继承一个父类。
注意:在C#中有一个关键字 sealed,用来修饰类,用它修饰的类是不能被继承的,我们称这中类为密封类。如常用的 string 类就是密封类。

is a 的引用

如果有两个类继承自一个类,如“A继承C,B继承C”那么我们可以定义一个" List "的集合 A 和 B 都可以加入到这个集合中,当要遍历集合时,我们可以对每个对象的类型进行判断,如:

SE ai = new SE("112","艾边成",25,100);
SE joe = new SE("113","Joe",30,200);
PM pm = new PM("890","盖茨",50,30);
List<Employee> empls = new List<Employee>();
empls.Add(ai);
empls.Add(joe);
empls.Add(pm);
foreach (Employee empl in empls)
{
    if (empl is SE)
    {
        Console.WriteLine(((SE)empl).SayHi());
    }
    if (empl is PM)
    {
        Console.WriteLine(((PM)empl).SayHi());
    }
 }

is 关键字:这个关键字用来判断对象是否属于给定的类型,如果属于返回 true ,否则返回false;
as关键字:用来强制转换数据类型,如:对象名 as 数据类型
关于继承的特点:

  1. 继承模拟了现实世界的关系,OOP中强调一切皆为对象,这符合我们面向对象编程的思考方向
  2. 继承实现了代码的重用
  3. 继承使得程序结构清晰,子类和父类的层次结构清晰

如果父类的子类非常多,子类的方法各个不同时,那么在程序中就要对众多的对象编写非常多的if语句进行判断,使程序变得庞大,扩展困难,这里就介绍一种虚方法

//修改父类的方法
public virtual string SayHi()
{
	//省略方法体
}
//修改子类的方法
public override string SayHi()
{
	//省略方法体
}
SE ai = new SE("112","艾边成",25,100);
SE joe = new SE("113","Joe",30,200);
PM pm = new PM("890","盖茨",50,30);
List<Employee> empls = new List<Employee>();
empls.Add(ai);
empls.Add(joe);
empls.Add(pm);
foreach (Employee empl in empls)
{
	Console.WriteLine(empl.SayHi());
}

虚方法中父类用 virtual 关键字来声明,子类中用 override关键字修饰的方法称为方法的重写,虚方法可以被重写的,方法的重写是实现多态的一种形式。
继承的优缺点
优点:

  1. 代码的复用
  2. 代码的扩展

缺点:

  1. 是侵入式编程
  2. 增加了类之间 耦合,子类必须依赖父类

多态

多态是指一种事物的多种形态,按字面意思就是多种形态,指同一操作作用于不同的对象时,可以有不同的解释,产生不同的执行结果。

实现多态

在C#中我们怎么实现多态:

  1. 方法的重载(前文有所记载)
  2. 方法的重写

方法的重载前面已经说到过了,现在说一下方法的重写

重写

重写的前提是两个类之间必须存在继承关系,子类重写父类的方法。
重写在C#中也分为两种重写的方法

  1. 虚方法
  2. 抽象方法

重写的特点

  1. 在不同的类中
  2. 相同的方法名和参数
  3. 重写的方法必须在父类中存在
  4. 被子类重写的方法必须在父类中明确的标注出关键字
  5. 如果子类当前是在重写父类的方法,那么必须用 override 关键字修饰
虚方法

注意

  1. 子类不一定要实现重写方法
  2. 虚方法在父类中是可以有方法体的
抽象方法

定义语法:

访问修饰符 abstract 返回值类型 方法名();

注意:抽象方法必须定义在抽象类中,在抽象方法中是不能有方法体的
存在的意义:规定子类必须重写父类的抽象方法
定义抽象类语法

访问修饰符 abstract class 类名
{

}

特点

  1. 抽象类不能被实例化
  2. 抽象方法没有方法体
  3. 子类必须重写父类的抽象方法,除非子类也是抽象类
  4. 抽象方法只能位于抽象类中

注意:每个类都一定有一个父类,没有显示定义,那么类的父类就是Object
在面向对象的编程中是低耦合,高内聚的。
有5大设计原则,可以百度查看一下。‘
好的谢谢观看。

你可能感兴趣的:(深入了解C#)