【C#4.0图解教程】笔记(第1章~第8章)
第1章 C#和.NET框架
1..NET框架的组成
.NET框架由三部分组成(严格来说只有CLR和FCL(框架类库)两部分),如图
执行环境称为:CLR(公共语言运行库),它在运行期管理程序的执行.
BCL(基类库)是.NET框架使用的一个大的类库.它包含以下这些类
通用基础类,集合类,线程和同步类,XML类.
2.编译成CIL
CIL(公共中间语言):一旦CIL被编译成本机代码,CLR就在它运行时管理它,执行像释放无主内存,检查数组边界,检查参数类型和管理异常之类的任务.
3.托管代码与非托管代码
托管代码:为.NET框架编写的代码称为托管代码,需要CLR
非托管代码:不在CLR控制之下运行的代码,比如WIN32 C/C++ DLL,称为非托管代码
4.CLR
CLR是.NET框架的核心组件,它在操作系统的顶层,负责管理程序和执行.
CLR还提供以下服务:
自动垃圾收集
安全和认证
通过访问BCL得到广泛的编程功能,包括和WEB服务和数据服务之类的功能.
5.CLI
CLI(公共语言基础结构)是一组标准,它把所有的.NET框架的组件连结成一个内聚的,一致的系统.它展示了系统的概念和架构.并详细说明了所有软件都必须坚持的规则和约定.
其中,CTS和CLS是CLI的重要组成部分
6.总结
第2章 C#编程概述
1.标识符与关键字
区分大小写,注意@字符
2.输出语法
eg:
Console.WriteLine("Three integers are {1},{0} and {1}",3,6)
3.注释
第3章 类型,存储和变量
1.预定义类型
有16种,其中13种简单类型,3中非简单类型
decimal:小数类型的有效数字为28位
dynamic:在使用动态语言编写的程序集时使用
2.用户定义类型
有6种类型可以由用户自己创建.它们是
3.栈和堆
运行中的程序使用两个内存区域来存储数据:栈和堆
栈是一个内存数组,是一个LIFO(后进先出)的数据结构
堆是一块内存区域,在堆里可以分配大块的内存用于存储某类型的数据对象.
4.值类型和引用类型
值类型只需要一段单独的内存,用于存储实际的数据.
引用类型需要两段内存:第一段存储实际的数据,它总是在堆中;第二段是一个引用,指向数据在堆中的存放位置.
对于引用类型的任何对象,它所有的数据成员都存放在堆里,无论它们是值类型还是引用类型.
5.C#类型的分类
6.静态类型和dynamic关键字
静态类型:变量的类型在编译的时候确定并且不能在运行时修改.
dynamic关键字:代表一个特定的,实际的C#类型,它知道如何在运行时解析自身,也就是说,它是动态化的静态类型.
7.可空类型
可空类型允许创建可以标记为有效或无效的值类型,这样就可以在使用它之前确定值的有效性.普通的值类型称作非可空类型
①创建可空类型
eg:
int? myNInt = 29;
第4章 类:基础
1.为数据分配内存
要为实际数据分配内存,需要使用new运算符.
new运算符为任意指定类型的实例分配并初始化内存.它依据类型的不同从栈或堆里分配
2.访问修饰符
5种
第5章 方法
1.字段与本地变量
字段通常保存和对象状态有关的数据.
本地变量通常用于保存本地的或临时的计算数据.
2.var关键字
var关键字并不是某种特别类型的符号,它只是句法上的速记,表示任何可以从初始化的右边推断出的类型.
var关键字的一些重要的条件:
①.只能用于本地变量,不能用于字段;
②.只能在变量声明中包含初始化时使用;
③.一旦编辑器推断出变量的类型,它就是固定且不能更改的.
3.本地常量
关键字const
两个特征:
①.常量在声明中必须初始化
②.常量在声明后不能改变.
③.只能被声明在类声明中.
④常量没有自己的存储位置,而是在编译时被编译器替换.类似于C++中的#define值
eg:
const int i = 0; |
4.返回值
经测试,声明了返回类型的方法必须使用return语句从方法返回一个值,返回值的类型应与声明返回类型一致或可以隐性转换.
不返回类型的方法可以使用void声明方法.
return;这种不带参数的形式的返回语句只能用于声明void类型的方法.
5.引用参数
关键字ref
①使用引用参数时,必须在方法的声明和调用中都是使用ref修饰符
②实参必须是变量,在用作实参前必须被赋值,如果是引用类型变量,可以赋值为一个引用或null值.
③引用参数的实参与形参用的是相同的内存,而值参数的实参与形参不是用相同内存.
eg:
过程:
6.输出参数
关键字out
①必须在声明和调用中都使用修饰符,输出参数的修饰符是out.
②实参必须是变量.
③在方法内部,输出参数在被读取之前必须被赋值,这意味着参数的初始值是无关的,而且没有比要像ref一样在方法调用之前为实参赋值.
eg:
过程:
7.参数数组
关键字params
①它允许零个或多个实参对应一个特殊的形参.
②在一个参数列表中只能有一个参数数组.
③如果有,它必须是列表中的最后一个.
④声明参数数组时在数据类型前需要使用params修饰符,在数据类型后放置一组空的方括号.调用时不允许有params修饰符.
⑤参数数组的方法调用,可以一个逗号分隔该数据类型元素的列表,也可以是该数据类型元素的一维数组.
⑥当实参是一维数组时,编辑器是使用这组数组而不是重新创建一个.
eg:列表作实参;
过程:
eg:数组作实参
8.参数类型总结
9.方法重载
一个类中可以有一个以上的方法拥有相同的名称,这叫做方法重载
使用相同名称的每个方法必须有一个和其他方法不相同的签名,可以为:
●方法的名称;
●参数的数目;
●参数的数据类型和顺序;
●参数修饰符;
注意:返回类型和形参名称都不是签名
10.命名参数
从C#4.0开始,只要显式指定参数的名字,就可以任意顺序在方法调用中列出实参.
eg:使用命名参数的结构
eg:如果是参数顺序不同的重载,使用命名参数则会报错.
public partial class _Default : System.Web.UI.Page
{
protected void Page_Load(object sender, EventArgs e)
{
A my = new A();
int a = my.s(b:4,a:4);//错误在以下方法或属性之间的调用不明确:“A.s(int, double)”和“A.s(double, int)”
}
}
class A
{
public long s(int a, double b)
{
int s = 0;
return s;
}
public long s(double b, int a)
{
int s = 0;
return s;
}
} |
11.可选参数
所谓可选参数就是在调用方法的时候包含这个参数,也可以忽略它.
可选参数只能是值参数类型
eg:
第6章 类进阶
1.静态字段/静态成员
关键字static
静态字段被类的所有实例共享,所有实例都访问同一内存位置.
实例不能访问静态成员,因为静态成员不属于某一对象,它只属于类本身.
2.属性
属性是一个函数成员,它不为数据存储分配内存,它执行代码.
属性是指定的一组两个匹配的,称为访问器的方法.
set访问器用于为属性赋值.
get访问器用于从属性取值.
属性也可以声明为static.静态属性的访问器和所有静态成员一样,不能访问类的实例成员,不管类是否有实例,它们都存在,还有从类外部访问时,必须使用类名引用.
3.构造函数
4.静态构造函数
①.静态构造函数声明中使用static关键字
②.类只能有一个静态构造函数,而且不能带参数.
③.静态构造函数不能有访问修饰符.
④.静态构造函数不能访问所在类的实例成员,因此也不能使用this访问器.
5.对象初始化
eg:
6.析构函数
析构函数执行在类的实例被销毁之前需要清理或释放非托管资源的行为.
每个类只能有一个析构函数.
析构函数只对类的实例起作用,因此没有静态析构函数.
不能在代码中显式调用析构函数.
一般不要用,占性能
7.Dispose
在类中实现名称为IDisposable的接口,接口中把资源的清理代码封装在一个void类型的无参数方法中,就是Dispose()方法.
因为是Dispose做清理而不是析构函数,所以它应当调用 GC.SuppressFinalize(this)方法,该方法告诉CLR不要调用该方法的析构函数,因为它已经被清理了.
标准的清理模式如下:
8.readonly修饰符
类似于const修饰符,一旦值被设定就不能改变.不过有区别
①const字段只能在字段的声明语句中初始化,而readonly字段在下列任意位置设置它的值.
●字段声明语句中,这点同const
●类的任何构造函数.如果是static字段,初始化必须在static构造函数中完成.
②const字段的值必须在编译期决定,而readonly字段的值可以在运行期决定.这种增加的自由性允许你在不同的环境或不同的构造函数中设置不同的值.
③与const不同,const总是像静态的,而对于readonly字段,它可以使实例字段,也可以是静态字段.而且它在内存中有存储位置.
eg:
9.this关键字
this关键字在类中使用,是对当前实例的引用.它只能被用在下列类成员的代码块中.
●实例构造函数
●实例方法
●属性和索引的实例访问器
不能用在任何静态成员中.this被用于下列目的:
●用于区分类的成员和本地变量,或参数;
●作为调用方法的实参.
10.索引
索引是一组get和set访问器,类似于属性的访问器.
索引总是实例成员,因此不能被声明为static
索引没有名称,在名称位置是关键字this.参数列表在方括号中间.参数列表中至少必须声明一个参数
索引还可以重载,类中重载的索引必须有不同的参数列表.
eg:
public class Employee
{
public string Lastname; //调用字段0
public string FirstName; //调用字段1
public string CityOfBirth;//调用字段2
public string this[int index] //索引声明
{
public set //set访问器声明,可以自己设置访问修饰符,这里设为public
{
switch (index)
{
case 0: Lastname=value;break;
case 1: FirstName = value;break;
case 2: CityOfBirth = value; break;
default: throw new ArgumentOutOfRangeException("index");
}
}
private get //get访问器声明,可以自己设置访问修饰符,这里设为private
{
switch (index)
{
case 0: return Lastname;
case 1: return FirstName;
case 2: return CityOfBirth;
default: throw new ArgumentOutOfRangeException("index");
}
}
}
} |
11.分部类和分部类型
关键字partial
每个分部类的声明都含有一些类成员的声明.
类的分部类声明可以在同一文件中也可以在不同文件中.
分部方法的例子
eg:
第7章 类和继承
1.隐藏基类的成员
使用new关键字以显式地告诉编译器掩盖基类成员.但如果要访问被隐藏的基类成员,可以使用base表达式来访问.
2.使用基类的引用
如果有一个派生类对象的引用,就可以获取该对象基类部分的引用,使用类型转换运算符把该引用转换为基类类型.
eg:
3.虚方法和覆写方法
当使用基类引用访问派生类对象时,得到的是基类的成员.虚方法可以使用基类的引用访问"升至"派生类内
可以使用基类引用派生类的方法,要满足下面条件
●派生类方法和基类方法有相同的签名和返回类型
●基类的方法使用virtual标注
●派生类方法使用override标注
其他关于virtual和override修饰符的重要信息如下
●覆写和被覆写的方法必须有相同的可访问性,换一种说法,被覆写的方法不能是private等,而覆写方法是public
●不能覆写static方法或非虚方法.
●继承的继承可以覆写override方法
eg:
4.继承的继承
eg:
如图:SecondDerived类中是new了一个print方法,当执行MyBaseClass mybc = (MyBaseClass)derived;时,方法调用只向上传递了一级而已,因为SecondDerived类中的print方法是new的
所以输出结果为:
如果把SecondDerived类中的print方法的new改成override,则结果为:
5.构造函数初始化语句
如果希望派生类使用一个指定的基类构造函数而不是无参数的构造函数,则必须在构造函数初始化语句中指定它,使用base关键字,见图
如果希望调用同一类中的另一个构造函数,可以使用this关键字,见图
6.类的可访问性
对类的可访问性,只有两种修饰符:internal和public.(其他修饰符使类没有访问性)
标记为public的类可以被系统内任何程序集中的代码访问,
而标记为internal的类只能被它自己所在的程序集内的类看到.这是默认的访问级别.
7.成员访问修饰符
8,抽象成员
抽象成员是被设计来被覆写的函数成员.
●它被用abstract修饰符标记.
●它没有实现代码块,抽象成员的代码块用分号表示.
●抽象成员只能在抽象类中声明
●尽管抽象成员必须在派生类中用相应的成员覆写,但不能把virtual修饰符附加到abstract修饰符.
●就想虚成员,派生类中抽象成员的实现必须指定override修饰符.
比较虚成员与抽象成员
9.抽象类
抽象类就是被设计来被继承的,抽象类只能被用作其他类的基类
●不能创建抽象类的实例
●抽象类使用abstract修饰符声明
●抽象类可以包含抽象成员或普通的非抽象成员.
●任何派生自抽象类的类必须使用override关键字实现该类所有的抽象成员,除非派生类自己也是抽象类.
eg:数据成员不可以声明为abstract
10.密封类
●密封类只能被用作独立的类,不能被用作基类
●密封类使用sealed修饰符标注
11.静态类
●静态类里的成员都是静态的.
●静态类本身必须标记为static
●静态类可以有一个静态构造函数,但没有实例构造函数,不能创建该类的实例
●静态类是隐式是密封的,所以不能被继承.
eg:
12.扩展方法
当一个类既是密封的(即不能继承)又不能修改的情况下,可以使用扩展方法给该类添加方法.
●声明扩展方法的类必须声明为static
●扩展方法本身必须声明为static
●扩展方法必须包含关键字this作为它的第一个参数类型,并在后面跟着它所扩展的类的名称
●扩展方法的结构
eg:
第八章 表达式和运算符
1.数字不具有布尔意义
2.浅比较与深比较
①浅比较:如果引用相等,也就是说,如果它们指向内存中相同对象,那么相等性比较为true,否则返回false,即使内存中两个分离的对象在所有其他方面都完全相等也为false
②深比较:如果两个字符串有相同的长度和相同的大小写敏感的内容,那么相等性比较返回true,即使它们占用不同的内存区域.
3.逻辑运算符
4.用户定义类型转换
①隐式转换语法如下:
eg:
②显式转换语法:
只要将隐式转换语法中的implicit替换为explicit
eg:
5.运算符重载
与用户定义隐式转换和显式转换类似.
只有以下这些运算符可以被重载.
eg:重载负数,减法和加法
6.typeof运算符
该运算符返回作为它的参数的任何类型的System.Type对象,