C#基础:C#4.0权威指南 杂笔一
1、c#中数组初始化的几种不同用法
int[] name = new int[NUM];
int[] name = {1, 2, 3, 4, 5, 6};
int[] name = newint[5]{1, 2, 3, 4, 5};
int[ , ] name = new int[3][4];
foreach(int name in names) ;
2、Nullable类型(合成类型)
Nullable 类型 = 基本类型 + "是否为nuil指示器";
如果一个类型既可以分配一个值,也可以分配null (没有任何值),那这个类型是可空的
3、dynamic类型
它允许其操作略过编译器类型检查,而在运行时处理。
优点:简化了对 COM API的调用,增强了对动态语言的支持简化了对HTML文档对象模型(DOM)的访问
dynamic 类型的变量被编译成Object类型的变量,因此,dynamic只存在于编译期,而运行时并不存在
4、溢出检查
checked 关键字用于对整形算数运算和转换显式启用溢出检查,而 unchecked 则相反
5、as 和 is 运算符
隐式转换是安全的,而显式转换是不安全的,往往会造成精度的丢失
有两种解决方法:
1> 使用 as 运算符进行类型转换
as 运算符用于在两个引用类型之间进行转换,如果转换失败返回null ,不抛出异常,只有在运行时可以通过是否返回null去判断
* 使用限制:只能用于引用类型或者可以为null的类型
2> 先使用 is 运算符判断类型是否可以转换,再使用()运算符进行显式的类型转换
is 运算符用于检查对象是否与给定类型兼容,并不执行真正的转换,如果判断对象引用为null,则返回false,不会抛出异常
* 使用限制:只适用于引用类型转换,或者装箱个拆箱转换,
* 工作中尽量少使用()运算符,不是很安全
用as或者is的话不会抛出异常,减少了系统开销
用 as 比 is 性能更好
* 适用场合:1>如果测试对象的目的是确定它是否属于所需类型,并且如果测试结果为真,就要立即进行转换,这时用as更好
2>如果仅仅只是用于测试,并不想立即转换,或者根本就不转换,只是在对象实现了接口时,要将它加入到一个
列表中,这时用 is 操作符更好
6、栈针
栈针也叫做过程活动记录,是编译器用来实现方法调用的一种数据结构,包含如下参数:
a、方法参数
b、方法中的局部变量
c、方法执行完后的返回地址
d、获取文件中包含所执行代码的行号和列号
e、获取包含所执行代码的文件名
7、几个必须的CIL汇编指令
8、this 关键字的三个用途
a、当局部变量名称和类字段相同时,用以引用类字段
b、将当前对象的实例作为参数传递到其他方法
c、可以声明索引器
* 由于静态成员属于类级别,对静态成员使用 this 是错误的
9、访问修饰符
10、构造函数
引用基类构造函数时, 使用 base()方法,引用自身构造方法时,使用this()方法
11、对象初始化列表
使用对象初始化列表是一种初始化对象数据的快捷方式。使用对象初始化列表可以在创建对象时,向对象的任何可访问的
字段或属性分配值,而无须显式调用构造函数。
12、析构函数
写法:~类名(); 析构函数在CIL中会被转换为 Finalize();
a、析构函数不嫩有访问修饰符
b、析构函数不能有参数
c、一个类只能有一个参数
d、无法继承或重载析构函数
e、无法调用析构函数
f、无法预知析构函数何时会被调用,因为它是被自动调用的
13、索引器
索引器和属性的异同:
14、分部类型和分部方法 ------修饰符:partial
就是将一个类型或者方法拆分到两个或多个源文件中,每个源文件中只包含类型定义的一部分。类、结构、接口、方法都可以拆分。
分部类特征:
a、类的定义前加 partial
b、分部类可以定义在两个不同的.cs文件中,也可以定义在一个.cs文件中
c、分部类必须同属一个命名空间
分部方法(有严格限制)特征:
a、声明必须以上下文关键字 partial开头
b、声明不能有访问修饰符,因此是隐式私有的
c、不能有返回值
d、可以有 ref 参数,不可以有 out 参数
e、分部方法可以使用 static 和 unsafe 修饰符
f、参数名称在实现声明和定义声明中虽然可以不同,但仍然推荐使用一致的方法签名
* 对没有实现的分部方法的调用都会被编译器移除,分部方法的典型应用是分类代码生成器生成的代码和用户编写的代码
15、XML注释文档的标记介绍
标记 示例 说明
<c> / <code> <code>content</code> 标注希望将其标记为代码的文本
<example> <example>说明</example> 代码示例,经常与<code>连用 <exception> 指明一个成员会抛出哪些异常,常与cref属性连用
<include> 指明注释在哪些文件中,以及位置
<list> 用来定义表头,常与<item>连用
<newpara>:内部使用,如<remarks>或<returns>。让用户有机会给注释文本加入其他的结构。
<param>:说明参数的属性,编译器会检查参数的合法性。如果通不过,会在文档中产生!标识的警告。
<paramref>:类似<param>。
<permission>:标明用于成员的代码存取的安全性。
<remarks>:用于描述class或其它类型的描述性文字,不涉及具体细节(如果是这样,使用<summary>)。
<returns>:描述方法或函数的返回值。
<see>:指定一个链接。
<seealso>:指定要出现在See Also部分的文本。
<summary>:类型的描述性文字。它会被vs.net内置的IntelliSense使用并显示在对应的类型中.(即在vs.net中击键“.”出现的提 示 )
<value>:描述属性。
标记及描述
cref:可用于任何标记来提供一个代码元素的参考。编译器将检查这个代码元素是否存在,如不存在则在文档中用!标识。
name:用于<param>或<paramref>
16、使用 new 修饰符显式的隐藏从基类继承的成员
若要隐藏继承的数据成员,需要在派生类中声明一个相同名称的成员,并使用 new 修饰符修饰该成员,只需名称相同即可,类型不必相同
若要隐藏继承的方法成员,需要在派生类中声明一个具有相同签名的方法,签名不包含返回值,并使用 new 修饰符修饰该方法
基类的静态成员也可以被隐藏
17、扩展方法
当我们对已有的类添加新功能时,可以选择从已有的类继承,然后在派生类中加入新的方法,也可以使用另外一个方法,就是用扩展方法,
扩展方法的特征:
a、是一种特殊的静态方法,必须定义于静态类中
b、第一个参数以this修饰符为前缀,后要跟扩展的目标类型及其形参
c、扩展方法所在的类必须在使用它的类可见范围内,否则需要使用using指令
d、扩展方法只能针对实例调用,目标类不能为静态类
e、如果扩展方法和非扩展方法中某个方法的签名相同,则扩展方法不会被调用
18、标志枚举
定义标志枚举,需要注意的两个地方:
a、使用Flag特性修饰,作用是对所修饰的元素起一个描述、说明作用
b、枚举各成员的值,应以2的幂进行赋值(1、2、4、8、16、. . .)
[System.Flags] //特性
public enum Mousebuttons
{
{
Left = 1; // 鼠标左键 0x0001
Right = 2; // 鼠标右键 0x0002
Middle = 4; // 鼠标中键 0x0004
}
class EnumSample
{
public static void main(string[] args)
{
// 标志字
Mousebuttons m = Mousebuttons.Left } Mousebuttons.Right | Mousebuttons.Middle;
System.Console.WriteLine("按下鼠标的键:{0}", m);
if ( (m & Mousebuttons.Left) == Mousebuttons.Left)
{
System.Console.WriteLine("左键被按下了。");
}
}
}
19、λ (Lambda)表达式和匿名方法基本相同,只是语法不同,它由匿名方法演变而来,但使用更简单
(param)=>expr // param -> 是一个输入参数列表, expr -> 是一个表达式或者一系列语句。
λ表达式的特性:
a、在一个具有唯一的显式类型参数的Lambda表达式中,圆括号可以用参数列表中删除
b、当输入参数不唯一时,则括号不能省略
c、输入参数列表中的各参数可以显式指定类型,也可以省略参数类型,具体类型通过类型推断机制判断
d、expr 可以只包含一个表达式,也可以包含一系列语句,不过需要用大括号括起来
1> 当expr为表达式时的λ表达式:
运行结果:
2> 当expr为语句时的λ表达式:
20、事件的初级使用
事件的声明有两种方式,一种是系统预定义的 委托类型,一种是用户自定义的:
a、预定义的委托类型:
// 关键字 委托类型 事件名
public event EventHandler PrintComplete;
EventHandler委托的签名如下:
public delegate void EventHandler(object sender,EventArgs e);
// sender -> 负责保存触发事件的对象的引用
// e -> 负责保存事件数据,这里是在BCL中定义的默认的EventArgs 类,它位于System命名空间中,
它不能保存任何数据,可以定义一个静态的数据成员( public static event EventHandler PrintComplete;)
【订阅事件】
事件订阅者需要订阅事件发布者发布的事件,才能在事件发布时,收到消息,如果事件使用预定义的委托类型:Eventandler,那
么匹配它的事件处理程序如下:
// 返回类型 这里可以是EventArgs的派生类
public void SomeEventHandler(Object sender, EventArgs e)
{ // . . . }
【触发事件】
b、自定义的委托类型
委托:public delegate void PrintEventDelegate (Object sender, PrintEventArgs e);
声明事件:public event PrintEventDelegate PrintComplete;
因为我们扩展的PrintEventArgs类没有不带参数的构造函数,因此需要修改事件触发部分的代码,传递一个参数进去,该参数的值
就是要发送给事件处理方法的状态信息,如下用字符串代替:
if(Print'Complete != null){
PrintComplete(this,new Print'EventArgs(“测试消息”));
}
【事件访问器】
21、从Array对象继承的成员
22、集合
【BCL中的集合分类】
a、非泛型,类和接口位于System.Collections;
b、泛型, 类和接口位于System.Collections.Generic;
* 每个非泛型集合接口都有一个泛型版本,但不一定每个泛型集合接口都有对应的非泛型版本。这个例外是,ISet<T>.
* 非泛型和泛型集合接口的说明
【常用的几种泛型和非泛型集合接口】
【常用的集合类型】
【ArrayList】
ArrayList实现了IList、ICollection、IEnumerable接口
ArrayList和Array比较类似,下面是两者的比较:
ArrayList的主要特性如下:
ArrayList的主要成员:
【Hashtable】
Hashtable实现了IDictionary、ICollection、IEnumerable接口,Hashtable表示键(key)/值(value)对的集合,每一对键值对保存在一个DictionaryEntry中,因此
Hashtable实际上是DictionaryEntry实例对象的集合。
× 需要注意的地方:
【Queue 和 Queue<T>】
Queue称为队列,实现了ICollection、IEnumerable接口,它的主要特性如下:
有四个公共的构造函数,如下所示:
23、正则表达式
【正则表达式字符转义列表】
【正则表达式定位点列表】
【正则表达式字符类列表】
【正则表达式限定符列表】
【正则表达式分组构造列表】
【正则表达式替换构造元素列表】
【.NET对正则表达式的支持】
【.NET提供的正则表达式相关核心类】
【简单使用】
24、预处理指令集
25、异步编程的四种方法:
【1、EndInvoke】
当使用BeginInvoke异步调用方法时,如果方法未执行完,EndInvoke方法就会一直阻塞,直到被调用的方法执行完毕。
代码如下:
using System;
using System.Threading;
namespace 异步线程的调用方法之一 {
class AsynchronousSample {
// 声明一个委托类型
public delegate void PrintDelegate(string content);
public static void Main(string[] args) {
int threadId = Thread.CurrentThread.ManagedThreadId;
PrintDelegate printDelegate = AsynchronousSample.Print;
Console.WriteLine("[主线程Id:{0}]\t开始调用打印方法. . .", threadId);
IAsyncResult result = printDelegate.BeginInvoke("hello,world!", null, null);
printDelegate.EndInvoke(result);
Console.ReadKey();
}
public static void Print(string content) {
int threadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("[当前线程Id:{0}]\t{1}", threadId, content);
System.Threading.Thread.Sleep(2000);
Console.WriteLine("[当前线程Id:{0}]\t打印方法调用完毕", threadId);
}
}
}
结果:
【2、WaitHandle】
代码如下:
using System;
using System.Threading;
namespace 异步线程的调用方法之二{
class AsynchronousSample{
// 声明一个委托类型
public delegate void PrintDelegate(string content);
public static void Main(string[] args){
int threadId = Thread.CurrentThread.ManagedThreadId;
PrintDelegate printDelegate = AsynchronousSample.Print;
Console.WriteLine("[主线程Id:{0}]\t开始调用打印方法. . .", threadId);
IAsyncResult result = printDelegate.BeginInvoke("hello,world!", null, null);
result.AsyncWaitHandle.WaitOne(5000, false);
Console.ReadKey();
}
public static void Print(string content){
int threadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("[当前线程Id:{0}]\t{1}", threadId, content);
System.Threading.Thread.Sleep(2000);
Console.WriteLine("[当前线程Id:{0}]\t打印方法调用完毕", threadId);
}}}
结果:
【3、轮询】
前面的两种方法只能等待异步线程的执行完毕,轮询可以通过检查 IA's'y'ncResult类型的I's'Comp'le'te'd属性来检查异步
调用是否完成,如果没有完成,可以给用户一些提示,提高用户体验
代码如下 :
using System;
using System.Threading;
namespace 异步线程的调用方法之三 {
class AsynchronousSample {
// 声明一个委托类型
public delegate void PrintDelegate(string content);
public static void Main(string[] args) {
int threadId = Thread.CurrentThread.ManagedThreadId;
PrintDelegate printDelegate = AsynchronousSample.Print;
Console.WriteLine("[主线程Id:{0}]\t开始调用打印方法. . .", threadId);
IAsyncResult result = printDelegate.BeginInvoke("hello,world!", null, null);
while (!result.IsCompleted){
Console.WriteLine("给用户的提示");
Thread.Sleep(500);
}
Console.ReadKey();
}
public static void Print(string content) {
int threadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("[当前线程Id:{0}]\t{1}", threadId, content);
System.Threading.Thread.Sleep(2000);
Console.WriteLine("[当前线程Id:{0}]\t打印方法调用完毕", threadId);
} } }
结果 :
【4、回调】
之前的三种方法都是等待线程调用完成后,主线程才能去做别的事,回调的好处是,可以不用等待,在调用Begin'Invoke时只要
提供了回调方法,主线程将不再等待异步线程的执行结果,而是去处理别的事情。
代码如下:
using System;
using System.Threading;
namespace 异步线程的调用方法之四{
class AsynchronousSample{
// 声明一个委托类型
public delegate void PrintDelegate(string content);
public static void Main(string[] args){
int threadId = Thread.CurrentThread.ManagedThreadId;
PrintDelegate printDelegate = AsynchronousSample.Print;
Console.WriteLine("[主线程Id:{0}]\t开始调用打印方法. . .", threadId);
IAsyncResult result = printDelegate.BeginInvoke("hello,world!", PrintComplete, printDelegate);
Thread.Sleep(10000);
Console.ReadKey();
}
public static void Print(string content){
int threadId = Thread.CurrentThread.ManagedThreadId;
Console.WriteLine("[当前线程Id:{0}]\t{1}", threadId, content);
System.Threading.Thread.Sleep(1000);
}
public static void PrintComplete(IAsyncResult asyncResult){
if (null == asyncResult) {
throw new ArgumentNullException();
}
int threadId = Thread.CurrentThread.ManagedThreadId;
(asyncResult.AsyncState as PrintDelegate).EndInvoke(asyncResult);
Console.WriteLine("[当前线程Id:{0}]\t打印方法调用完毕", threadId);
}}}
结果:
26、IO操作
【File】提供文件操作的静态方法
【Directory】提供目录操作的静态方法
需要 using System.IO
FileStream fs = null; // 打开文件时返回一个流
StreamWrite write = nul; //写文件
string dirPath = "f:/ioSample"
string filePath = string.Format(@"[0]/{1}", dirPath, "mytest.txt");
// 判断该目录是否存在,如果不存在则创建
if (!Directory.Exits(dirPath)){
Directory.CreateDirectory(dirPath);
}
//判断该目录下是否有该文件存在
if (!File.Exits(filePath)){
fs = File.Create(filePath);
}
// 打开文件
fs = File.Open(filePath, FileMode.Open);
fs.seek(0, SeekOrigin.End);
write = new StreamWrite(fs);
write.WriteLine("写入一行数据");
/*
以字符串数组的形式返回文件中的所有行,该方法只适用于比较小的文件,较大的文件打开时,在将所有的数据加载到
内存中之前,ReadAllLines()会一直阻塞,知道加载完成才能读取第一行的数据,这样不但内存使用效率低下,还会
延迟对行的处理
*/
string[] fileLines = File.ReadAllLines(filePath);
/*
上述方法的改进,使用 <string> 而不是 string[], .NET 4新增一个方法:
IEnumerable<string> lines = File.ReadLines(filePath);
该方法每次只读取一行的数据,大大的提高了效率,而且可以根据需要提前终止循环,免得获取不需要的行
*/
// .NET4 对目录读取时的改进
// 每次读取该目录下的一个文件信息
IEnumerable<FileInfo> files = directory.EnumerateFiles();
// 关闭写
write.Close();
// 关闭数据流
fs.Close();
【FileInfo】提供文件操作的实例方法
【DirectoryInfo】提供目录操作的实例方法
FileInfo和Directory代表某一个具体的文件或者目录,它们都派生自抽象类FileSystrmInfo,因此不能被实例化,FileSystemInfo
的主要成员如下:
27、流(Stream)
涉及流的三个基本操作:读取流,写入流,查询流
Stream类的主要成员如下:
上述成员大致分为三类:抽象成员(抽象属性和抽象方法)、虚成员(虚方法)、一般成员
Stream类的主要派生关系:
【文件操作中的异步操作】
FileStream类有15个构造方法,只有一个是可以使用异步I/O的,如下所示:
// userAsync 选择使用异步I/O还是同步I/O
public FileStream(string path,FileMode mode,FileAccess access,FileShare share, int bufferSize,
bool userAsync);