深入.NET框架和C#编程
深入.NET框架
Microsoft.NET框架概述
1.1Microsoft.NET介绍
2000年,微软公司向全球提供其具有革命性的软件和服务平台,这对消费者、企业和软件开发商来说,预示着个人将获得更大的能力和充满更多商业机会的到来。
Microsoft.NET平台利用以互联网为基础的计算和通信激增的特点,通过先进的软件技术和众多的智能设备,从而提供更简单、更为个性化、更有效的互联网服务。.NET的战略目标是在任何时候(when)、任何地方(where)、使用任何工具(what)都能通过.NET获得网络上的任何信息,享受网络带给人们的便捷与快乐。
1.2.NET框架的优势
- 提供了一个面向对象的编程环境,完全支持面向对象编程。.NET框架提高了软件的可复用性、可扩展性、可维护性和灵活性。
- 对Web应用的强大支持,如今是互联网时代,大量的网络应用程序发挥着重要作用。比如红极一时的社交网站Myspace使用.NET开发的,.NET平台的强大功能与稳定性使得它可以面对庞大的用户群体的访问。
- 对Web Service(Web服务)的支持。Web Service是.NET非常重要的内容,它可以实现不同应用程序之间的相互通信。
- 实现SOA,支持云计算。SOA是一个重要的架构范例,它支持中间层解决方案的模块化实现。
- 支持构建.NET程序的炫彩外衣。随着科技的发展,人们越来越多地使用计算机软件进行信息化办公,也越来越重视良好地用户体验和视觉效果。.NET提供WPF技术,帮助开发人员创建良好地3D效果。WPF提供了丰富地用户界面(User Interface,UI)框架,集成了矢量图形和丰富地流动文字支持。
.NET框架体系结构
.NET框架运行在操作系统之上,是.NET最基础地框架。它提供了创建、部署和运行.NET应用地环境,主要包含公共语言运行时(CLR)和框架类库(.NET Framework类库,FCL),并且支持多种开发语言。
CLR
CLR全称公共运行语言时,它是所有.NET应用程序运行时的环境,是所有.NET应用程序都要使用的编程基础。
CLR的组成:
**CLS(公共语言规范):**每种编译语言可能都会在类型、语法或语言规范上有着区别,CLS限制了有这些不同点引发的互操作性问题,它是一种最低的语言标准,制定了一种以.NET平台为目标的语言所必须支持的最小特征,以及该语言与其他.NET语言之间实现互操作所需要的完备特征,凡是遵守这个标准的语言在.NET框架下都可以实现相互调用。
**CTS(通用类型系统):**用于解决不同语言数据类型不同的问题,如C#中的整型是int,而VB.NET中的整型是integer。通过CTS将它们编译成通用类型Int32。
**MSIL(中间语言):**也叫.NET编译技术。为了实现跨语言开发和跨平台的战略目标,.NET所编写的应用都不编译成本地代码,而是编译成微软中间代码(Microsoft Intermediate Language,MSIL)。
FCL
FCL(框架类库)提供对系统功能的调用,是建立.NET应用程序、组件和控件的基础。
.NET的核心类库:
System : 此命名空间包含所有其他的命名空间。
System.Collect.Generic : 支持泛型操作。
System.IO :支持对文件的操作。
System.NET : 支持对网络协议的编程。
System.Data : 提供对表示ADO.NET结构类的访问。
System.Windows.Forms : 用于开发Windows应用程序。
System.Drawing : 支持GDI+基本图形操作。
面向对象
1.类和对象
类和对象有着本质的区别,类定义了一组概念的模型,而对象是真正的实体。
它们之间的关系如下:
由对象归纳类,是归纳对象共性的过程。
在类的基础上,将状态和行为实体化为对象的过程称为实例化。
对于类的属性,通过get和set访问器进行访问和设置:
只写属性:只包含set访问器。
只读属性:只包含get访问器。
读写属性:同时包含get和set访问器。
示例:
private string _name;
public string Name
{
get{return _name;}
set{_name = value;}
}
2.封装
封装(Encapsulation)又称为信息隐藏,是指利用抽象数据类型将数据的操作结合在一起,使其构成一个不可分割的独立实体,尽可能隐藏内部的细节只保留一些对外接口,使之与外部发生联系。
封装带来的好处:
保证数据的安全性
提供清晰的对外接口
类内部实现可以任意修改,不影响其他类
3.类图
作用:简洁、直观地表示众多类的结构及类与类之间的联系,将类的属性和行为以图的行为展示出来,使读者不用阅读大量的代码即可明白类的功能及类之间的关系。
深入C#数据类型
值类型和引用类型
值类型
值类型源于System.ValueType家族,每个值类型的对象都有一个独立的内存区域用于保存自己的值,它所在的区域称为栈(Stack)。值类型包括基本数据类型(如int,double,floa)和枚举类型等。
引用类型
引用类型源于System.Object家族,在C#中引用类型主要包括数组、类和接口等。
class Program
{
static void Main(string[] args)
{
int[] infoZhang = new int[] { 170, 60 };
int[] infoLi = infoZhang;
Console.WriteLine("去年--张浩的身高是:" + infoZhang[0] + "体重是:" + infoZhang[1] + ",李明的身高是:" + infoLi[0] + "体重是:" + infoLi[1]);
infoLi[0] = 180;
infoLi[1] = 70;
Console.WriteLine("今年--张浩的身高是:" + infoZhang[0] + "体重是:" + infoZhang[1] + ",李明的身高是:" + infoLi[0] + "体重是:" + infoLi[1]);
Console.ReadLine();
}
}
对引用类型,赋值就是把原对象的引用传递给另一个引用。对数组而言,当一个数组引用赋值给另一个数组引用后,这两个引用指向同一个数组,也就是指向同一块存储空间。
细分值类型和引用类型
值类型 | 引用类型 | ||
---|---|---|---|
基本数据类型 | 整型:int | 类 | 基类:System.Object |
长整型:long | 字符串:string | ||
浮点型:float | 自定义类:class | ||
双精度型:double | 接口 | 接口:interface | |
字符型:char | 数组 | 数组:int[],string[] | |
布尔型:bool | |||
枚举类型 | 枚举:enum | ||
结构类别 | 结构:struct |
结构
结构是值类型,声明结构变量就存储一个结构的新副本,即系统要开辟一块新的存储空间,因此结构用得越多所消耗的存储空间也越多。
访问修饰符 struct 结构名
{
//结构体
}
结构的定义有以下特点:
1.结构可以有方法也可以有字段
2.定义时,结构中的字段不能被赋初始值
结构的使用
结构的构成和类相似,在使用时注意以下几点:
1.可以不用new,直接定义结构的对象
2.声明结构的对象后,必须给结构的成员赋初始值
装箱和拆箱
拆箱:拆箱是引用类型转化为基本类型
装箱:装箱是基本类型转化为引用类型
static void Main(string[] args)
{
int i = 123;
object o = i;//装箱
i = 456;//改变i的内容
Console.WriteLine("值类型的值为{0}",i);
Console.WriteLine("引用类型的值为{0}", o);
}
static void Main(string[] args)
{
int i = 123;
object o = i;//拆箱
int j = (int)o;
Console.WriteLine("j的值现在为:" + j);
Console.ReadLine();
}
不同类型的参数传递
使用值传递,在方法中对参数值的更改在调用后不保留。
使用ref方式传递,可以保留对参数值的更改。
值方式参数传递
在值方式传递参数时,参数可以是引用类型,也可以是值类型。
使用引用类型作为参数
class Program
{
public void Vote(Class1 se)
{
//人气值增加1
se.Popularity++;
}
static void Main(string[] args)
{
Class1 zhang = new Class1();
zhang.Age = 25;
zhang.Name = "张靓";
zhang.Gender = "1201";
zhang.Popularity = 10;
//投票前
MessageBox.Show(zhang.SayHi());
Program voter = new Program();
voter.Vote(zhang);//引用类型做参数
//投票后
MessageBox.Show(zhang.SayHi());
}
}
注意:当类作为参数传递时,参数被修改,类成员的值也会被修改。
使用值类型作为参数
class Program
{
public void Vote(StructSE se)
{
//人气值增加1
se.Popularity++;
}
static void Main(string[] args)
{
StructSE zhang = new StructSE();
zhang.Age = 25;
zhang.Name = "张靓";
zhang.Gender = "1201";
zhang.Popularity = 10;
//投票前
MessageBox.Show(zhang.SayHi());
Program voter = new Program();
voter.Vote(zhang);//值类型做参数
//投票后
MessageBox.Show(zhang.SayHi());
}
}
注意:以值类型作为参数进行值方式传递参数时,不能改变值类型参数的值。
引用方式参数传递
在形参和实参前都加ref,那么它们都会保存方法中的修改。
public void ShowInfo(ref int a){
a = 5;
}
static void Main(string[] args)
{
int a = 0;
MessageBox.Show(ShowInfo(ref a));
}
//输出结果为5
小结:
使用值方式(不用ref修饰)传递值类型参数时,参数在方法中的修改不会保留。
使用值方式(不用ref修饰)传递引用类型参数时,参数在方法中的修改会保留。
使用引用方式(不用ref修饰)传递值类型或引用类型参数时,参数在方法中的修改都会保留。
使用集合组织相关数据
集合概述
ArrayList
ArrayList非常类似于数组,也有人称它为数组列表,ArrayList可以动态维护。它的索引会根据程序的扩展而重新进行分配和调整。ArrayList提供了一系列方法对其中的元素进行访问、增加和删除操作。
语法:
ArrayList 集合名= new ArrayList();
ArrayList类属于System.Coolections命名空间,使用它使一定要导入命名空间。ArrayList是动态可维护的,因此定义时既可以指定容量,又可以不指定容量。
using System.Coolections;
ArrayList stu = new ArrayList();//创建容量为0的ArrayList对象
ArrayList stu1 = new ArrayList(5);//创建容量为5的ArrayList对象
ArrayList常用属性和方法:
属性名 | 说明 |
---|---|
Count | 获取ArrayList中实际包含的元素数 |
返回值 | 方法名 | 说明 |
---|---|---|
int | Add(Object value) | 将对象添加到ArrayList的结尾处 |
void | Remove(int index) | 移除ArrayList指定索引处的元素 |
void | RemoveAt(Object vaule) | 从ArrayList中移除特定元素 |
void | Clear() | 从ArrayList中移除所有元素 |
遍历ArrayList:
//for:通过下标循环输出
for(int i = 0; i < ArrayList.Count; i++)
{
Console.WriteLine(ArrayList[i]);
}
//foreach:通过Object包含ArrayList循环输出
foreach(Object obj in ArrayList)
{
Console.WriteLine(obj);
}
Hashtable
Hashtable的数据结构统称为哈希表,也有人称它为“字典”,Hashtable的数据时通过键(Key)和值(Value)来组织的。Hashtable属于System.Coolections命名空间,它的每一个元素都是键/值对。
语法:
Hashtable 集合名= new Hashtable();
Hashtable常用属性和方法:
属性名 | 说明 |
---|---|
Count | 获取包含在Hashtable中的键/值对的数目 |
Keys | 获取包含在Hashtable中键的集合 |
Values | 获取包含在Hashtable中值的集合 |
返回值类型 | 方法名 | 说明 |
---|---|---|
void | Add(Object key,Object values) | 将带有指定键和值的元素添加到Hashtable中 |
void | Remove(Object key) | 从Hashtable移除带有特定键的元素 |
void | Clear() | 从Hashtable中移除所有元素 |
遍历Hashtable:
由于Hashtable不能用索引访问,所以遍历Hashtable只能使用foreach()方法。
Hashtable ht = new Hashtable();
//遍历Value
foreach (Object cvb in ht.Values)
{
Console.WriteLine(cvb);
}
//遍历key
foreach (int item in ht.Keys)
{
Console.WriteLine(item);
}
//遍历Key和Value
foreach(DictionaryEntry en in ht)
{
Console.WriteLine(en.Key + en.Value);
}
泛型和泛型集合
泛型是C#2.0中的一个新特性。泛型引入了一个概念:类型参数,通过使用类型参数,大量减少了运行时强制转换或装箱操作的风险,通过泛型可以最大限度地重用代码,保护类型的安全和提高性能,它们最常见应用就是创建集合类,泛型集合可以约束它所储存的对象类型。
泛型集合List<T>
语法:
List<T> 对象名 = new List<T>();
T可以对集合中的元素类型进行约束,T表明集合中管理的元素类型。
List<T>常用的方法和属性:
属性名 | 说明 |
---|---|
Count | 获取List中实际包含的元素数 |
返回值 | 方法名 | 说明 |
---|---|---|
int | Add(T value) | 将对象添加到List的结尾处 |
void | Remove(int index) | 移除List指定索引处的元素 |
void | RemoveAt(T vaule) | 从List中移除特定元素 |
void | Clear() | 从List中移除所有元素 |
遍历List<T>:
List<T> lsit = new List<T>();
for(int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
foreach(T item in list)
{
Console.WriteLine(item);
}
泛型List与非泛型ArrayList的区别:
异同点 | List<T> | ArrayList |
---|---|---|
不同点 | 对所保存元素进行类型约束。添加/读取值类型元素无需拆箱和装箱。 | 可以增加任何类型。添加/读取值类型元素需拆箱和装箱。 |
相同点 | 通过索引访问集合的元素。添加、删除元素方法相同。 |
泛型集合Dictionary<K,V>
泛型集合Dictionary<K,V>它具有泛型的全部特征,编译时检查类型约束,获取元素无需进行类型转换,存储方式和Hashtable相似,也是通过Key/Value(键/值)对元素保存的。
语法:
Dictionary<K,V> 对象名 = new Dictionary<K,V>();
Dictionary<K,V>常用的方法和属性:
同Hashtable的方法和属性一致。
泛型Dictionary<K,V>与非泛型Hashtable的区别:
异同点 | Dictionary<K,V> | Hashtable |
---|---|---|
不同点 | 对所保存元素进行类型约束。添加/读取值类型元素无需拆箱和装箱。 | 可以增加任何类型。添加/读取值类型元素需拆箱和装箱。 |
相同点 | 通过Key获取Values。添加、删除和遍历元素方法相同 |
泛型类
语法:
public class 类名<T>
{
//...
}
T是类型参数,代表具体的数据类型,可以是类参数,也可以是基本数据类型。
泛型的优点:
1、性能高:泛型无需进行类型的转换操作。
2、类型安全:泛型集合对它所存储的对象进行了类型的约束。
3、实现代码的重用:泛型相当于模板,它支持任何数据类型。
深入类的方法
构造函数
类的构造函数是类中的一种特殊方法,它具有以下特点:
1.方法名与类名相同
2.没有返回值类型
3.主要完成对象的初始化工作
构造函数的作用:实例化对象
带参构造函数
它比无参构造函数的灵活性更好,它通过参数来动态控制对象的特征,避免了给众多属性赋值带来的麻烦。
语法
访问修饰符 类名(参数列表)
{
//方法体
}
无参构造函数
在默认的情况下,系统将会给类分配一个无参构造函数,并且没有方法体。也可以自定义一个无参构造函数,在无参构造函数的方法体中对类的属性进行赋值。
语法:
访问修饰符 类名()
{
//方法体
}
隐式构造函数
当不给类编写构造函数时,系统将自动给类分配一个无参构造函数,称为隐式构造函数。
方法重载
构造函数的重载
多个构造函数提供多种实例化一个类的方式称为构造函数重载。
在面向对象的语言中,允许我们在同一个类中定义多个方法名相同、参数列表(参数个数、参数类型)不同的方法,称为方法重载。调用时会根据实际传入的参数的形式,选择与其匹配的方法执行。构造函数的重载时方法重载的一种特殊方式。
方法重载的特点:
1.方法名相同。
2.方法参数类型不同或者参数个数不同。
3.在同一个类中。
方法重载示例:
public class Student{
public Car(string name,string id,int age)
{
}
public Car(string name, string id, int age,int number)
{
}
}
对象交互
在面向对象的世界里,一切皆为对象。对象与对象相互独立,互不干涉,但在一定外力作用下,对象开始共同努力。
每个类都有自己的特性和功能,我们把它们封装为属性和方法。对象之间通过属性和方法进行交互。可以认为方法的参数及方法的返回值都是对象间相互传递的信息。
继承和多态
继承概述
什么是继承
当两个类描述相同属性的代码时,如果要扩展这个程序,加入一些其他的类,编码时因为类的不同会编写大量关于相同属性的重复代码,造成冗余,随着系统规模的扩大,冗余将会越来越多,如何避免这种冗余,把冗余代码集中起来重复利用呢?
继承的概念
在C#中,一个类可以继承另一个类。继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用已存在的类的功能。继承是面对对象编程中一个非常重要的特性,再有继承关系的两个类中,子类不仅具有自己独有的成员,还具有父类的成员。
public class Employee{ //父类
public string Name{get;set;}
public string NUmber{get;set;}
}
public class SE:Employee{ //子类
this.Name = "小明";
this.Number = "S1";
//在子类调用父类的成员
}
Base关键字和protected修饰符
在继承关系中如何去访问父类的成员呢?this表示当前实例,通过它可以访问类本身的成员。
C#中还有一个关键字base,它表示父类,可以用于访问父类的成员。
public class School{
public string Name{get;set;}
private string Number{get;set;}
}
public class Student:School{
base.Name = "小明同学";
base.Number = "SS7"; //编译报错
}
在以上代码中,在子类中可以用base调用父类的属性。实际上,还可以用base关键字调用父类的方法或构造函数。
父类的成员如果用private修饰,它将作为私有成员,其他任何类都无法访问。
C#中提供了另一种访问修饰符protected,被这个访问修饰符修饰的成员允许被其他子类访问,而不允许其他非子类访问。
public class School{
public string Name{get;set;}
protected string Number{get;set;}
//只需将访问修饰符改为protected就不会报错了
}
public class Student:School{
base.Name = "小明同学";
base.Number = "SS7";
}
修饰符 | 类内部 | 子类 | 其他类 |
---|---|---|---|
public | 可以 | 可以 | 可以 |
protected | 可以 | 可以 | 不可以 |
private | 可以 | 不可以 | 不可以 |
子类构造函数
1.隐式调用父类构造函数
创建子类对象时会首先调用父类的构造函数,然后才会调用子类本身的构造函数,如果没有指明要调用父类的哪一个构造函数,系统默认调用父类的无参构造函数。
public class School{
public School(){
Console.WriteLine("姓名:"+Name);
}
public string Name{get;set;}
public string Number{get;set;}
}
public class Student:School{
base.Name = "小明同学";
base.Number = "SS7";
public Student(){
Console.WriteLine("学号:"+base.Number);
}
}
//这段代码会先输出姓名,再是学号
2.显示调用父类构造函数
只要在子类的构造函数后添加“base(参数列表)”,就可以指定该子类的构造函数调用父类的哪一个构造函数了。
public class School{
public string Name{get;set;}
public string Number{get;set;}
public School(string name,string number){
Console.WriteLine("姓名:" + Name + "学号:" + Number);
}
}
public class Student:School{
public Student(string name,string number):base(name,number){ //调用父类含两个参数的构造函数
this.Name = name;
this.Number = number;
}
}
继承的使用
继承的特性
1.继承的传递性
举例:小型卡车is a卡车,卡车is a汽车,所以小型卡车is a汽车,这就是继承的传递性。
2.继承的单根性
在C#中明确规定一个子类不能同时继承多个父类。
is a的应用
is关键字用来判断对象是否属于给定的类型,如果属于返回true,否则返回false。
if(empl is SE) //表示判断empl对象是否是SE类型
继承的价值
1.继承模拟了现实世界的关系,OOP中强调一切皆对象,这符合我们面向对象编程的思考方向。
2.继承实现了代码的重用性。
3.继承使得程序结构清晰,子类和父类的层次结构清晰,最终的目的是使子类只关注子类的相关行为和状态,无须关注父类的相关行为和状态。
多态
解决继承带来的问题
当一个父类中含有多个子类,每个子类中的方法相同,参数个数,类型不同时,那么在程序中就要对众多的对象编写非常多的is关键字的if判断语句了,使程序变得庞大,扩展困难。
什么是多态
从面向对象编程的角度思考,三种不同的对象对于同一个方法调用表现出了不同的行为。
多态按字面的意思就是“多种形态”,指同一操作作用于不同的对象时,可以有不同的解释,产生不同的执行结果。
里氏替换原则(LSP):
是软件设计应该遵守的重要原则之一,有了它,才使继承复用成为可能,只有当子类可以替换父类时,软件的功能不受影响,父类才能真正被复用。
实现多态
重载:
同一个多态的实现方式。
**特点:**在同一个类中,相同的方法名,参数的个数、类型不同。
重写:
**前提:**两个类存在继承关系
不同类多态的实现方式。
**特点:**在不同类中,相同的方法名,参数的个数、类型不同。
1.虚方法
语法:
访问修饰符 virtual 返回类型 方法名(){
//方法体;
}
//在父类中用virtual
访问修饰符 override 返回类型 方法名(){
//方法体;
}
//在子类中用override
2.抽象方法
抽象方法是一个没有实现的方法
语法:
访问修饰符 abstract 返回类型 方法名();
**注意:**含有抽象方法的类必定是抽象类!
虚方法和抽象方法的区别
虚方法 | 抽象方法 |
---|---|
用virtual修饰 | 用abstract修饰 |
要有方法体,即使是一个分号 | 不允许有方法体 |
可以被子类override | 必须被子类override |
除了密封类外都可以定义 | 只能在抽象类中定义 |
抽象类
父类不被实例化,只提供方法的定义,让子类实现方法。
语法:
访问修饰符 abstract class 类名(){
}
特点:
1.不能用sealed和static修饰。sealed表示禁止继承,C#中静态方法只能通过类名去点,static类中只能有static方法。
2.不能实例化(new)对象。
3.抽象类可以有抽象方法,可以有非抽象方法。