第一章 深入.NET框架
一 、 .NET框架体系结构
1.NET框架的结构
.NET框架也就是.NET FrameWork,主要包含公共语言运行时(CLR)和框架类库(FCL)。CLR包含两个组成部分:
CTS:通用类型系统,如C#中的整型是int,而VB.NET中的整型是Integer,通过CTS我们把它们两个编译成通用的类型Int32。
CLS:公共语言规范,如在C#中命名是区分大小写的,而在VB.NET中不区分大小写,这样CLS就规定,编译后的中间代码除了大小写之外还要有其他的不同之处。
MSIL:中间语言,vs统一能认识的规范语言。
FCL:框架类库,有些已经被封装好的功能的集合。
二、类和对象
1.类和对象的概念
类: 具有相同特性和行为的对象组成的集合就是类,实际上类就是一个数据类型。
对象:一个具体存在的实物,世间万物皆为对象。
2.类和对象的关系:
类是对象的抽象化,对象是类的实例化。
public class Student{} //这是一个学生类
static void Main(string [] args)
{
Student s1=new Student(); //类的实例化-->对象
}
3.类的成员:
字段:一般把类或结构中定义的变量和常量叫字段,建议私有化。
属性:get只读属性,set只读属性,可以同时拥有,也可以只存在其中一个。
方法:语法:访问修饰符 返回值类型 方法名(参数列表){}
public class Student
{
private string _name; //字段
public string Name { get => _name; set => _name = value; } //属性
//方法
public void SayHi(){}
}
分为两种:
静态方法:使用static关键字来标识静态方法。
非静态方法:同样,非静态方法就是不使用static关键字修饰。
区别:1.静态方法不能调用非静态方法。
2.调用:静态方法只能通过类名点方法名来访问,不能通过对象名点方法名访问。
非静态方法的调用可以通过对象名点方法名访问。
public class Employee
{
public static void SayHi(){}
public void ShowHobby(){}
static void Main(string [] args)
{
Employee.SayHi(); //静态方法通过类名点方法名调用
//Employee.ShowHobby(); //会报错,静态方法不能调用非静态方法
Employee e1 =new Employee();
e1.ShowHobby(); //非静态方法的调用可以通过对象名点方法名访问。
}
}
①访问修饰符
public:公开的,人人都能使用。
protected:受保护的,在继承中,一般用于父类,类的受保护成员只能由类本身和类的子类内部访问,相当于有密码的WiFi。
private:私有的,只能在类内部使用,相当于自己的流量。
类的默认访问修饰符:internal
类成员的默认访问修饰符:private
②返回值类型:数据类型+void
void:void表示方法没有返回值,这时注意不能用return返回值。
数据类型:return必须存在,而且必须有值,值的类型必须和返回值类型兼容。
③参数:分为两种:
形参:定义方法时传入的参数,形参是抽象的。
实参:调用方法时传入的参数,实参的数据类型必须和形参一致,参数个数、顺序也相同。
public void Swap(){}
Swap(); //调用
public void Swap1(int a){}
Swap1("1"); //报错,实参形参数据类型不一致
Swap1(1);
public void Swap2(int a,int b){}
Swap2(1); //报错,实参和形参数目不同
Swap2(1,2);
4.类的三大特性:
封装:将类内部构造私有化。
继承:子类继承父类。
多态:在同一范畴内,一个事物有多种形态。
第二章 深入C#数据类型
一 、值类型和引用类型
1.值类型
<1>、命名空间:System.ValueType;
<2>、保存区域:每个值类型的对象都有一个独立的内存区域用于保存自己的值,值类型数据所在的内存区域称为栈,值变地址也变,一个字段占用一个栈;
<3>、包括基本数据类型、枚举类型(enum)、结构类型(struct);
2.引用类型
<1>、命名空间:System.Object;
<2>、保存区域:引用类型存储对值的引用,数据所在的内存区域称为堆,值变地址不变,字段怎么改变也是原来的地址;
<3>、包括类、接口、数组
3.区别:
值类型:不同的变量分配不同的储存空间,一个变量的值改变不会影响另外一个变量。 //指向栈 开辟了新地址
引用类型:不同的变量分配相同的储存空间,一个引用类型的值改变也会影响另一个变量。//指向了堆 使用了原来的地址。
int heightZhang = 170;
int heightLi =heightZhang;
heightLi=180;
//int是值类型,首先系统会给heightZhang分配存储空间,然后系统还会给heightLi分配存储空间,所以改变heightLi的值不是影响heightZhang的值
int [] array = new int [5];
int []array1=array; //这里把array的引用地址也给了array1,所以array1指向的地址和array一样,array1的值变,array的值也会变
int [] array=new int[5]
int [] array1=new int[5] //array1开辟了一个新的空间
array1[0]=array[0]; //因为array1开辟了新的空间,而且这里是给的下标值,所以array1的值变array的值不会随着改变,因为指向的地址不同
二、结构
1、结构的定义:
访问修饰符 struct 结构名{}
2、结构的特点:
①结构中可以有字段,也可以有方法,但是很少写属性;
②定义时,结构中的字段不能被赋初始值
3、结构的使用:
①可以不用new,直接定义结构的对象即可;
②声明结构的对象后,必须给结构的成员赋初始值。
4、注意:①结构不能不支持继承;
②在没有new的时候,不能定义属性,并且不能调用方法;
③结构的构造函数必须是有参数构造函数;
④结构和类相似,但是结构属于值类型,而类属于引用类型。
//例如
public struct Student
{
public int _id; //字段
public int _age; //不能赋初始值
public void Study(){}
static void Main(string [] args)
{
Student stu;
stu._id=111;
stu.Study(); //报错,在没有new时候不能调用方法
}
}
三、装箱和拆箱
1、装箱:将值类型转换成引用类型
2、拆箱:将引用类型转换成值类型
四、参数传递
1、值方式参数传递
值方式参数传递就是没有ref关键字修饰,参数可以是引用类型,也可以是值类型。
①使用引用类型作为参数
class Program
{
static void Main(string[] args)
{
Number a = new Number();
Add(a);
Console.WriteLine(a.i);
Console.ReadLine();
}
static void Add(Number a)
{
a.i++;
}
}
class Number
{
public int i = 10;
}
//输出结果 :11
以上代码可以看出,以引用类型作为参数进行值方式传递参数时,会改变引用类型参数的值。
②使用值类型作为参数
static void Main(string[] args)
{
//值传递
int number = 20;
Fun(number);//实参
Console.WriteLine(number);
Console.ReadLine();
}
static void Fun(int num)//形参,值类型作为参数
{
num += 20;
}
//输出结果:20
以上代码可以看出,以值类型作为参数进行值方式传递参数时,不能改变值类型参数的值。
2.引用方式参数传递
引用方式参数传递就是有ref关键字修饰,参数同样可以是引用类型,也可以是值类型。
①使用值类型作为参数
static void Main(string[] args)
{
int number = 20;
Fun(ref number);//实参
Console.WriteLine(number);
}
static void Fun(ref int num)//形参
{
num += 20;
}
//输出结果:40
class Program
{
static void Main(string[] args)
{
Number a = new Number();
Add(ref a);
Console.WriteLine(a.i);
Console.ReadLine();
}
static void Add(ref Number a)
{
a.i++;
}
}
class Number
{
public int i = 10;
}
//输出结果 :11
以上代码可以看出,使用引用方式(用ref关键字修饰)传递值类型和引用类型参数时,参数在方法中的修改都会保留。注意,在引用方式参数传递时,实参和形参的前面都必须加上ref关键字。
第三章 使用集合组织相关数据
一、ArrayList
1.ArrayList的定义:ArrayList非常类似于数组,可以保存一组大小不固定、数据类型不相同的数据的集合。
注意:由于ArrayList不限定数据类型,所以经常涉及数据类型转换、拆箱、装箱,这是ArrayList的优势同样也是劣势。
2.命名空间:System.Collections
3.如何创建ArrayList?
ArrayList list =new ArrayList();
4.ArrayList的属性和方法
属性:Count 说明:获取ArrayList的长度
返回值类型 | 方法名称 | 说明 |
---|---|---|
int | Add(Object value) | 添加元素,用来保存值 |
void | RemoveAt(int index) | 通过下标删除元素,下标从0开始,最后一个值的下标:长度-1 |
void | Remove(Object value) | 通过指定元素直接删除 |
void | Clear() | 清空ArrayList中的数据 |
//例如
ArrayList list =new ArrayList(); //创建ArrayList集合
list.Add("悠悠"); //下标为0
list.Add(17); //存值 //1
list.Add('女'); //2
Console.WriteLine(list.Count); //打印长度 输出:3
list.Remove("悠悠"); //删除指定元素
list.RemoveAt(2); //运行报错,因为在这之前删除了“悠悠”,所有元素都会向前移,因此不存在下标为2的元素
list.Clear(); //清空
Console.WriteLine(list.Count); //打印长度 输出:0
5.ArrayList的遍历
//for循环
for(int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
//foreach循环
foreach(Object obj in list)
{
Console.WriteLine(obj)
}
二、List
1.定义:保存一组大小不固定,但限定数据类型的数据的集合。
2.命名空间:System.Collections.Generic
3…创建:List list =new List(); T就是Type类型的意思,表示任意一种数据类型
4.List的属性和方法
属性:Count 说明:获取ArrayList的长度
返回值类型 | 方法名称 | 说明 |
---|---|---|
int | Add(T value) | 添加元素,当newList的时候T是什么数据类型,就只能添加什么数据类型 |
void | RemoveAt(int index) | 通过下标删除元素,下标从0开始,最后一个值的下标:长度-1 |
void | Remove(T value) | 删除指定的元素 |
void | Clear() | 清空集合中的元素 |
List<string> list =new List<string>();
list.Add(666); //报错,T为string类型,666是int类型,数据类型不一致
list.Remove("你好"); //不报错,虽然我没添加到集合中,但是无影响!!!
//注意:当移除一个不存在的值,其实不会有任何错误。
5.List的遍历
//for循环
for(int i = 0; i < list.Count; i++)
{
Console.WriteLine(list[i]);
}
//foreach循环
foreach(T item in list)
{
Console.WriteLine(item)
}
三、ArrayList和List的区别
1.相同点:
①相同的属性:Count
②相同的方法:Add,Remvoe,RemoveAt,Clear
③相同的遍历方式
2.不同点:
①List对储存的元素类型进行约束,添加/读取值类型元素无需拆箱、装箱
②ArrayList可以添加任何类型的元素,添加/读取值类型元素需要拆箱、装箱
四、HashTable
1.定义:用来保存key:value,大小不固定,并且可以添加任意数据类型的元素。
2.命名空间:System.Collections
3.创建:HashTable ht=new HashTable();
4.HashTable的属性和方法
属性名称 | 说明 |
---|---|
Count | 获取HashTable的长度 |
Keys | 获取包含在HashTable中键的集合 |
Values | 获取包含在HashTable中值的集合 |
返回值类型 | 方法名称 | 说明 |
---|---|---|
void | Add(Object key,Object values) | 添加元素 |
void | Remove(Object key) | 删除指定的key |
void | Clear() | 清空元素 |
bool | ContainsKey(Object key) | 查找指定的key是否已经存在 |
bool | ContainsValue(Object key) | 查找指定的value是否已经存在 |
HashTable ht =new HashTable();
ht.Add(17,"悠悠"); //键是学号,值是姓名
ht.Add(01,"喵喵");
ht[01]="菜菜"; //修改值,前提必须存在
5.HashTable遍历
//遍历key
foreach(Object obj in ht.Keys)
{
Console.WriteLine(obj+"----"+ht[obj]); //通过key获取值
}
//遍历value
foreach(Object item in ht.Values)
{
Console.WriteLine(item) //直接获取值
}
//同时获得key和value
foreach(DictionaryEnary en in ht )
{
Console.WriteLine(en.Key+"----"+en.Value)
}
注意:1、key如果已经存在了,再次添加,会报运行错误,说明key不能重复
2、null不能作为key
五、Dictionary
1.定义:用来保存key:value,大小不固定,但是限定数据类型的数据的集合。
2.命名空间:System.Collections.Generic
3.创建:Dictionary<Tkey,Tvalue> dic =new Dictionary<Tkey,Tvalue>();
4.Dictionary的属性和方法:和Hashtable一样
5.Dictionary遍历
//遍历key和遍历value和HashTable一样
//同时遍历key和value
foreach(KeyValuePair en in dic)
{
Console.WriteLine(en.Key+"----"+en.Value)
}
六、Hashtable和Dictionary的区别
1.相同点:
①相同的属性:Count,Keys,Values;
②相同的方法:Add,Remvoe,Clear,ContainsKey,ContainsValue;
③相同的遍历方式
2.不同点:
①Hashtable没有限定类型;
②Dictionary限定了key和value的数据类型
Dictionary<string,string> dic =new Dictionary<string,string>();
dic.Add("17","悠悠");
dic.Add(01,"菜菜"); //编译错误,Dictionary中的键是string类型的,01是int类型,数据类型不一致
dic.Add(null,"明明"); //运行错误,null不能作为键
dic.Add("01","菜菜");
foreach(string en in dic.Keys)
{
Console.WriteLine(en+"----"+ht[en]); //通过key获取值
}
bool flag=dic.ContainsKey(02); //查找集合中是否已经存在02键,存在返回true,不存在返回false
七、泛型和非泛型的区别
泛型:限制了数据类型,比如List和Dictionary。
非泛型:没有限制数据类型,比如ArrayList和Hashtable.
八、自定义泛型类
1、泛型类的作用是将类中的属性进行限制;
2、语法:public class 类名{}
public T name; //属性
当你实例化对象时:
类名 对象名 = new 类名();
此时进入的是什么类型,类中的属性就是什么类型.
九、集合绑定dgv
1.Lis绑定dgv:this.dgv.DataSource=new BindingList();
2.Dictionary绑定dgv:
BindingSource bs =new BindingSource();
bs.DataSource=dic.Values;
this.dgv.DataSource=bs;
注意:使用Datagridview绑定集合
这里的属性需要加:{get;set;}才能绑定成功!
例如:
public string name { get; set; }
public string id { get; set; }
第四章 深入类的方法
一、构造函数
1.语法:访问修饰符 类名(){}
注意:①类似方法,只是少了一个返回值,这里是没有返回值,若为void也是错误的,void代表返回值类型为空;
②方法名有要求:跟类名一致;
③构造函数也是会被重载。
2.作用:用来new对象,兼职:给成员变量赋值
3.分类:
①显示方式:
隐式:
1、就是创建一个类,没有手写构造函数的时候当前类自带的构造函数
2、就是无参的构造函数
显示:自定义构造函数
②参数的格式:有参和无参
4.特点:一个类中,如果没有手写的构造函数,那么这个类中有一个隐藏的无参构造函数,如果一旦手写了构造函数,那么这个类就不存在有隐藏的构造函数。
5.给成员变量赋值的方式:
①对象初始化器
static void Main(string[] args)
{
//对象初始化器赋值
Student stu = new Student(){name="悠悠",age=18};
}
class Student
{
public string name;
public int age;
}
②点符号
static void Main(string[] args)
{
//点符号赋值
Student stu = new Student();
stu.name="悠悠";
stu.age=18;
}
class Student
{
public string name;
public int age;
}
③构造函数
static void Main(string[] args)
{
//构造函数赋值
Student stu = new Student("悠悠",18); //调用Student类中的有参构造函数
}
class Student
{
public string name;
public int age;
public Student(){} //无参构造函数
public Student(string name,int age)
{
this.name=name;
this.age=age;
}
}
二、方法重载
1.实现多态的方式:①重载②重写
2.特点(定义):在同一个类中,方法名相同,对应的参数的下标参数类型不同。
3.重载的应用:MessageBox,Equals(可以尝试去看源代码,深入理解重载)
4.存在的意义:减少方法名的开辟,同样避免我们本身英语单词不足这一问题。
5.调用规则:根据参数【参数个数+数据类型】调用
6.注意:
不能以【返回值类型】来决定是否是重载
不能以【访问修饰符】来决定是否是重载
//方法重载的使用
static void Main(string[] args)
{
Calc c1=new Calc();
Console.WriteLine(c1.Pay(1, 9)); //输出:10
Console.WriteLine(c1.Pay(1.1, 5.9)); //输出:7
Console.WriteLine(c1.Pay("你好,", "世界")); //输出:你好,世界
Console.ReadLine();
}
class Calc
{
public Calc(){}
public int Pay(int a,int b)
{
int sum =a+b;
return sum;
}
public double Pay(double a,double b)
{
double sum =a+b;
return sum;
}
public string Pay(string a,string b)
{
string sum =a+b;
return sum;
}
}
第六章 初识继承和多态
一、继承
1.概念:描述两个类之间的关系,子类是继承的类,子类又叫派生类,父类是被继承的类,父类又叫基类或者超类。
2.语法:访问修饰符 class 子类:父类
3.访问修饰符:
public:公共的、公开的
protected:受保护的,被这个访问修饰符修饰的成员允许被其子类访问,而不允许其他非子类访问。
private:私有的,只能在类的内部使用。
注意:一旦产生继承关系,子类可以访问所有父类非私有成员。
4.构造函数
所有子类的构造函数都会调用父类的构造函数,默认情况下调用父类的无参构造函数,也可以显示的调用父类的构造函数。使用base关键字:
在子类的构造函数括号外使用:base()则表示显示调用父类的无参构造函数。
:base(值)则表示显示调用父类的有参构造函数。
注意:使用base调用父类的有参构造函数时,()里面的值是实参,而不是形参!
public class Person
{
public string Name{get;set;}
public int Age{get;set;}
//构造函数
public Person(){}
public Person(string name,int age)
{
this.Name=name;
this.Age=age;
}
}
//子类
public class Student:Person
{
public string Hobby{get;set;}
public Student():base(){} //显示调用父类无参构造函数,没什么意义:因为默认情况下子类会调用父类的无参构造函数
public Student(string name,int age,string hobby):base(name,age)//显示调用父类的有参构造函数
{
this.Name=name;
this.Age=age;
this.Hobby=hobby;
}
}
注意:一旦手写了构造函数,那么这个类就不存在有隐藏的无参构造函数。所以当子类调用父类的构造函数时,一定要看清楚父类的构造函数!
5.base和this的区别
this是指当前类的成员的引用;base是指在当前类中调用父类的成员。
6.继承的特性:
①单根性:每一个类都只能有一个父类;
②传递性:孙子可以拥爷爷辈的基因
注意:每一个类如有没有显示定义父类,都有一个默认的父类:Object;如果某一个类想禁止被继承,在class前面使用关键字:sealed(密封类)
7.is和as
is a是判断对象是否属于给定的数据类型,返回布尔类型,如果属于返回true,否则返回false。
语法:引用名[对象名] is 数据类型;
as a是用来强制转换
语法:数据类型 变量名 = 对象名 as 数据类型;
等价于 数据类型 变量 = (数据类型)对象名;
public class Employee
{
public void SayHi()
{
Console.WriteLine("大家好!");
}
}
public class SE:Employee
{
Console.WriteLine("大家好!我是员工");
}
public class PM:Employee
{
Console.WriteLine("大家好!我是经理");
}
static void Main(string [] args)
{
List<Employee> empls =new List<Employee>();
//省略初始化代码
foreach(Employee emp empls)
{
if(emp is SE)
{
SE se =emp as SE;
Console.WriteLine(se.SayHi());
}
if(emp is PM)
{
PM pm =emp as PM;
Console.WriteLine(pm.SayHi());
}
}
}
二、多态
1.概念:在同一范畴中,一个事物的多种形态。
2.实现多态的方式:重载和重写。
3.重写:
1.前提:继承关系
2.特点:在不同的类中,相同的方法名和相同的参数,返回值类型也相同,但是方法体不同。
3.标注方式:①虚方法;②抽象方法
4.虚方法:
①实现方式:父类的方法用关键字virtual修饰,子类将重写的方法使用override关键字。
②注意:父类的方法必须有方法体
public class Person
{
public Person(){}
public virtual void SayHi()
{
Console.WriteLine("大家好!");
}
}
//子类
public class Student:Person
{
public int ID{get;set;} //学号
public Student(){}
public override void SayHi()
{
Console.WriteLine("大家好!我的学号是{0}",this.ID);
}
}
第七章 深入理解多态
一、里氏替换原则
1.里氏替换原则的概念:所有引用父类的地方,都可以透明的使用子类对象。
2.继承的优点:①提供了代码的重用性,子类拥有父类的属性和方法;
②提供了代码的可扩展性,子类可以形似父类,也异于父类保留自我个性。
3.继承的缺点:①继承侵入式编程【子类拥有父类的属性和方法】
只要有继承关系,父类里面所有动作子类也一定有,实际上父类约束了子类的行动。
②增加了耦合性
二、抽象类和抽象方法
1.抽象类:
①语法:访问修饰符 abstract class 类名{}
②抽象类的特点:不能实例化对象,抽象类不能使用sealed和static修饰,抽象类中可以有抽象方法也可以有非抽象的方法。
2.抽象方法:
①定义:访问修饰符 abstract 返回值类型 方法名(参数列表){}
②特点:在不同的类中,方法名相同和参数相同,返回值类型也相同,抽象方法没有方法体,并且只能位于抽象类中,父类里面的抽象方法子类必须实现,除非子类也是抽象的。
③实现方式:前提在抽象中,父类的方法用关键字abstract修饰,子类被重写的方法使用关键字override修饰。
3.存在的意义:规定子类必须重写父类的方法。
//父类:动物类
public abstract class Animal
{
//抽象方法
public abstract void Eat();
}
public class Monkey:Animal
{
//子类必须重写父类的方法
public override void Eat()
{
Console.WriteLine("猴子吃香蕉!");
}
}
三、虚方法和抽象方法的区别
1.相同点:都是在约定子类拥有某个动作
虚方法:只能约定子类的动作可以是父类实现的。
抽象方法:只能子类自己实现,因为抽象方法没有方法体。
2.不同点:
不同点 | 定义关键字不同 | 方法体的要求 | 对类要求的不一样(密封类除外) | 对子类的实现不一样 |
---|---|---|---|---|
虚方法 | virtual | 必须有方法体 | 可以位于任意类 | 不强制要求子类必须实现 |
抽象方法 | abstract | 不能有方法体 | 只能位于抽象类 | 子类必须实现,除非子类也是抽象 |
四、软件设计原则
1.里氏替换原则:父类的引用指向子类的对象
2.单一职责原则:一个类只做一件事
3.依赖倒置原则:细节依赖抽象,而不是抽象依赖于细节
4.接口隔离原则:接口:只有抽象方法,
5.开闭原则:对修改关闭,对扩展开放