20170710——20170716
.Net & .Net Framework:.NET是Microsoft的用以创建XML Web服务(下一代软件)平台,该平台将信息、设备和人以一种统一的、个性化的方式联系起来,而可以在这个平台上开发的语言有很多,C#就是其中比较常用的一种。
.Net平台可分为公共语言运行时(CLR)和.Net类库,.Net Framework框架是.Net平台中不可缺少的一部分,提供了一个稳定的运行环境来保证我们基于.Net平台开发的各种应用程序正确运转。
.Net能干什么?
开发桌面应用程序(winform);
Internet应用程序(ASP.Net)
.Net的两种交互模式:
B/S & C/S
一、C#基本语法
1、修饰符
修饰符static表示方法不能在类的实例上执行,因此不必先实例化类再调用。例如:WriteLine()方法,把一行文本写到控制台窗口,它是一个静态方法,在调用之前不需要实例化Console对象;
2、变量的初始化——对象
实例化一个引用对象需要使用new关键字把该引用指向存储在堆上的一个对象;
3、C#预处理器指令
C#并没有独立的预处理器,所谓的预处理器指令实际上是由编译器处理的。
4、类和结构
结构和类的区别是它们在内存中的存储方式、访问方式(类是存储在堆上的引用类型,而结构是存储在栈上的值类型)和它们的一些特征(如,结构不支持继承)
5、函数成员方法
1、ref 参数
通过值传送变量是默认的,也可以迫使之参数通过引用传送给方法。为此,要使用ref关键字。如果把一个参数传递给方法,且这个方法的输入参数前带有ref关键字,则该方法对变量所做的任何改变都会影响原始对象的值;
能够将一个变量代入一个方法中进行改变,改变完成后,再将改变后的值带出方法,要求在方法外必须为其赋值,方法内可以不赋值。
2、out 参数
在方法的输入参数前面加上out前缀时,传递给该方法的变量可以不初始化。该变量通过引用传递,所以在从被调用的方法中返回时,对应方法对变量进行的任何改变都会保留下来。在调用该方法时,还需要使用out关键字,与在定义该方法时一样;
如果在一个方法中,返回多个相同类型的值的时候,可以考虑返回一个数组;但如果返回多个不同类型的值的时候,可以考虑使用out参数,out参数侧重于在一个方法中可以返回多个不同类型的值,要求在方法内部必须赋初值。
3、params 可变参数
将实参列表中跟可变参数数组类型一致的元素都当做数组的元素去处理,只能将其作为最后一个参数传递;
4、方法的重载
- 两个方法不能仅在返回类型上有区别;
- 两个方法不能仅根据参数是声明为ref还是out来区分
6、函数成员属性
private int age;
public int AGE
{
get
{
return age;
}
set
{
age = value;
}
}
1、只读和只写属性
在属性定义中省略set访问器,就可以创建只读属性;
2、属性的访问修饰符(p78)
3、自动实现的属性
4、内联
JIT编译器可生成高度优化的代码,并在适当的时候随意地内联代码。
5、构造函数
如果提供了带参数的构造函数,编译器就不会自动提供默认的构造函数。只有在没有定义任何构造函数时,便一起才会自动提供默认的构造函数。如果试图使用无参数的构造函数实例化类的对象,就会得到一个编译错误。
(1)静态构造函数
- 静态构造函数只执行一次,而构造函数时实例构造函数,只要创建类的对象,就会执行它;
- 类有一些静态字段或属性,需要在第一次使用类之前,从外部源中初始化这些静态字段和属性;
- 静态构造函数没有访问修饰符,其他C#代码从来不调用它,但在加载类时,总是由.NET运行库调用它,所以像public或private这样的访问修饰符就没有任何意义。
- 静态构造函数不能带任何参数,一个类也只能有一个静态构造函数。
- 静态构造函数只能访问类的静态成员,不能访问类的实例成员;
- 无参数的实例构造函数与静态构造函数可以在同一个类中同时定义。尽管参数列表相同,但这并不矛盾,因为在加载类时执行静态构造函数,而在创建实例时执行实例构造函数,所以何时执行哪个构造函数不会有冲突;
- 如果多个类都有静态构造函数,先执行哪个静态构造函数就不确定。此时静态构造函数中的代码不应依赖于其他静态构造函数的执行情况。另一方面,如果任何静态字段有默认值,就在调用静态构造函数之前指定他们。
(2)只读字段
有时可能需要一些变量,其值不应改变,但在运行之前其值是未知的——只读字段可解决;
readonly关键字允许把一个字段设置为常量,但还需要执行一些计算,以确定它的初始值。可以在构造函数中给只读字段赋值,但不能在其他地方赋值。只读字段还可以是一个实例字段,而不是静态字段,类的每个实例可以有不同的值。与const不同,如果要把只读字段设置为静态,就必须显式声明它。
7、输出相关
使用占位符{0},{1},…,{n}控制输出,多挖少填会抛出异常。
8、数据类型相关
隐式转换 VS 显式转换(Convert类);
枚举——可以规范开发:将枚举声明到namespace下面,类的上面,枚举就是一个变量类型,默认从0开始,和int可强转。
9、异常捕获
先写程序再抛异常,看哪一行有可能抛异常再用try…catch;
10、结构
结构体内,若字段前不加public,默认为private,只能在结构体内访问,出了结构之外就访问不到了。
字段和变量都是用来存储数据的,但是最本质的区别是,程序运行时,字段可以根据不同对象有不同输出,可以存储多个值,字段命名前要加_.
11、方法
关于参数调用:
如果被调用者想要得到调用者的值:
传递参数;
使用静态字段来模拟全局变量,在类名下,函数外使用;
12、字符串
C#中两个字符串的比较可以直接使用==;
二、线程
1、定义
多线程,使程序得以将其工作分开,独立运作,不互相影响
1、使用多线程需要注意的问题
它们可以同时运行,但如果线程访问相同的数据,就很容易出问题。必须实现同步机制。
2、异步委托(委托线程)
创建线程的一种简单方式是定义一个委托,并异步调用它。委托使用线程池来完成异步任务。
委托要调用某方法,必须定义一个有相同参数和返回类型的委托;
如果在委托结束之前不等待委托完成其任务就结束主线程,委托线程就会停止;
异步委托包含不同的技术并返回结果:
投票;
等待句柄;
异步回调;
3、Thread类
使用Thread类可以创建和控制线程。
给线程传递一些数据可以采用两种方式。一种方式是使用带ParameterizedThreadStart委托参数的Thread构造函数,另一种方式是创建一个自定义类,把线程的方法定义为实例方法,这样就可以初始化实例的数据,之后启动线程。(p513)
在默认情况下,Thread类创建的线程是前台线程。线程池中的线程总是后台线程。
在用Thread类创建线程时,可以设置IsBackground属性,以确定该线程是前台线程还是后台线程。
4、线程的优先级
线程调度器:操作系统根据优先级来调度线程。
5、线程池
不需要自己创建,由ThreadPool类托管。这个类会在需要时增减池中线程的线程数,直到最大的线程数。
6、线程问题
争用条件&死锁
如果两个或多个线程访问相同的对象,或者访问不同步的共享状态,就会出现争用条件。
*、System.Threading.Thread类
System.Threading.Thread是用于控制线程的基础类,通过Thread可以控制当前应用程序域中线程的创建、挂起、停止、销毁;
ManagedThreadId是确认线程的唯一标识符;
.NET为线程设置了Priority属性来定义线程执行的优先级别,里面包含5个选项,其中Normal是默认值。除非系统有特殊要求,否则不应该随便设置线程的优先级别;
Thread 中包括了多个方法来控制线程的创建、挂起、停止、销毁,以后来的例子中会经常使用;
在System.Threading中的包含了下表中的多个常用委托,其中ThreadStart、ParameterizedThreadStart是最常用到的委托。
由ThreadStart生成的线程是最直接的方式,但由ThreadStart所生成并不受线程池管理;
使用Thread.Start()启动的线程默认为前台线程,而系统必须等待所有前台线程运行结束后,应用程序域才会自动卸载。
线程Thread有一个属性IsBackground,通过把此属性设置为true,就可以把线程设置为后台线程!这时应用程序域将在主线程完成时就被卸载,而不会等待异步线程的运行。
2、进程
进程含有内存和资源。进程本身并不能够执行,它只是提供一个安置内存和线程的地方
3、原子操作
一个操作(operation)如果能够不受中断地完成,我们称之为 atomic operation
4、使用多个线程的结果
多线程程序无法预期;
执行次序无法保证——线程彼此之间的执行顺序应该视之为随机;
Task Switches 可能在任何时刻任何地点发生;
线程对于小的改变有高度的敏感
5、核心对象(即内核对象)
CreateThread( )传回来的 handle 被称为一个核心对象(kernel object);
只有一种 handle 可以代表核心对象。所谓handle,其实是个指针,指向操作系统内存空间中的某样东西,那东西不允许你直接取得。你的程序不能够直接取用它,为的是维护系统的完整性与安全性;
Win32 说明文件一再强调线程分为 GUI 线程和 worker 线程两种。GUI 线程负责建造窗口以及处理主消息循环。
6、被激发的对象(Signaled Objects)
当线程正在执行时,线程对象处于未激发状态。当线程结束时,线程对象就被激发了。因此,任何线程如果等待的是一个线程对象,将会在等待对象结束时被调用,因为当时线程对象自动变成激发状态。
7、同步&异步(synchronous & asynchronous)
当程序1调用程序2时,程序1停下不动,直到程序2完成回到程序1来,程序1才继续下去,这就是所谓的“synchronous”。如果程序1调用程序2后,径自继续自己的下一个动作,那么两者之间就是所谓的“asynchronous”。
8、Critical Sections(关键区域、临界区域)
Critical Section是广义地指一块内存、一个数据结构、一个文件,或任何其他具有“使用之排他性”的东西。也就是说,“资源”每一次(同一时间内)只能够被一个线程处理;
如果我再也不释放资源(或不离开 critical section,或不释放 mu tex……等等),会怎样?答案是:不会怎样! 操作系统不会当掉。用户不会获得任何错误信息。最坏的情况是,当主线程(一个 GUI 线程)需要使用这被锁定的资源时,程序会挂在那儿,动也不动;
由于 critical section 不是核心对象,如果 进 入 critical section 的 那 个 线 程 结 束 了 或 当 掉 了 , 而 没 有调 用 LeaveCriticalSection() 的话,系统没有办法将该 critical section 清除。
9、Mutex
Mutex 的拥有权是容易引人迷惑的地方。Mutex 的拥有权并非属于那个产生它的线程,而是那个最后对此 mutex 进行 Wait…() 操作并且尚未进行 ReleaseMutex() 操作的线程。线程拥有 mutex 就好像线程进入critical section 一样。一次只能有一个线程拥有该 mu tex;
10、同步机制摘要
Critical Section
Critical section(临界区)用来实现“排他性占有”。适用范围是单一进程
的各线程之间。它是:
- 一个局部性对象,不是一个核心对象。
- 快速而有效率。
- 不能够同时有一个以上的 critical section 被等待。
- 无法侦测是否已被某个线程放弃。
Mutex
Mutex 是一个核心对象,可以在不同的线程之间实现“排他性占有”,甚
至即使那些线程分属不同进程。它是:
- 一个核心对象。
- 如果拥有 mutex 的那个线程结束,则会产生一个 “abandoned” 错误信息。
- 可以使用 Wait…() 等待一个 mutex。
- 可以具名,因此可以被其他进程开启。
- 只能被拥有它的那个线程释放(released)。
Semaphore
Semaphore 被用来追踪有限的资源。它是:
一个核心对象。
没有拥有者。
可以具名,因此可以被其他进程开启。
可以被任何一个线程释放(released)。
11、线程优先权
进程的优先级类别(priority class)
大部分程序使用 NORMAL_PRIORITY_CLASS 。少数情况下才会考虑使用其他类别。例如,Task Manager 就是使用 HIGH_ PRIORITY_CLASS,所以即使其他程序处于非常忙碌的状态下,它也总是能够有所反应。
线程的优先级层级(priority level)
一共有七种优先权层级,优先权层级可以利用 SetThreadPriority () 改变之;
主线程不断等待,所以不断需要 CPU 时间。而由于它的优先权比 wo rker 线程高,所以 wo rker 线程永远没有机会获得 CPU 时间。这种情况称为 starvation (饥饿);
12、句柄(Handle)
Handle即句柄,最早翻译为“把手”,有以下特点:
1.虽然你握住的只是把手,却能拉动整扇门,而且你根本不用在意那门长什么样子
2.一扇门如果有多个把手,被不同的人(进程)握住,门往哪儿走就不好说了
13、overlapped I/O
overlapped I/O 是 Win32 的一项技术,你可以要求操作系统为你传送数据,并且在传送完毕时通知你。这项技术使你的程序在 I/O 进行过程中仍然能够继续处理事务。事实上,操作系统内部正是以线程来完成 overlapped I/O;
三、面向过程 VS 面向对象——封装、继承、多态
如果我们用面向过程的思想来解决某
件事,当执行这件事的人不同,我们需要为每个不同的人量身定做解决事情的方法。
面向对象:找个对象帮你做事儿;——屏蔽所有人差异
类是模子,确定了对象应该有的特征(属性)和行为(方法)。
1、类
字段(fields)、属性(properties)、方法(methods)(包括构造方法)
写好类之后需要创建类的对象,使用关键字new,this代表当前类的对象;
非静态类是不占内存的,而对象是占内存的,即对象的字段是占内存的。
属性
属性作用就是保护字段,对字段赋值和取值进行限定,属性写在类内;
属性的本质:通过属性对字段赋值;
字段默认访问权限为private,只能在当前类的内部进行访问,出了这个类就访问不到了。
当给属性赋值的时候首先会执行set方法;
当输出属性值的时候首先会执行get方法。
2、静态和非静态的区别
非静态类中
既可以有实例成员(非),也可以有静态成员;
在调用实例成员时,需要使用对象名.实例名();
在调用静态成员时,需要使用类名调用;
静态函数中,只能访问静态成员,不允许访问实例成员;
实例函数中,既可以使用静态成员,也可以使用实例成员;
静态类中
在静态类中,只能写静态成员;
静态类不能被实例化
什么时候用静态类?
如果想要类作为“工具类”,则声明为静态类;
静态类在整个项目中资源共享,静态类是占内存的;
静态类什么时候会释放资源?
GC(Garbage Collection)垃圾回收器
——只有在程序全部结束之后,静态类才会释放资源;