执行环境称为CLR: Common Language Runtime
公共语言运行库
基类库BCL 超集 框架类库FCL
CIL common intermediate language 公共中间语言
以前叫IL 或 MSIL
程序集要么是可执行的,要么是DLL
.NET写的代码由CLR来托管代码
所有C#关键字全部由小写字母组成,但是.NET类型名使用Pascal大小写约定
第29页 C#的关键字,C#的上下文关键字
程序的入口点
static void Main(){}
Console.WriteLie(“Two sample are {0} and {1}.”,3,6);
Console.WriteLie(“Two sample are {1}, {0} and {1}.”,3,6);
在控制台输出Two sample are 6, 3 and 6
第34页格式化数字字符串 挺重要的
{index,alignment:format}
第35页的那张表很关键
Console.WriteLine(“0:N2”,12345678.546321);
输出:123,456,789.54
文档注释 /// 就是三个斜杠
与C和C++不同,在C#中的数值类型不具有布尔意义
array和delegate类型不含有命名成员
46页的值类型和引用类型写的可以。
对于引用类型的任何对象,它所有的数据成员都存放在堆里,无论
它们是值类型还是引用类型。
第48页,尼码,array string object dynamic class interface delegate 是引用类型
重要!!!
dynamic是用来兼容IronPython和IronRuby之类的脚本语言的,这种动态类型
直到运行时才会被解析。
class Dealer{
…
}
Dealer theDealer; //声明了一个引用变量,但没有为数据存储在堆中分配内存
theDealer=new Dealer(); //为类对象分配内存并赋值给变量
在c和c++中,可以先声明一个本地变量,然后在嵌套块中声明另一个相同名称的本地变量
在内部范围,内部名称掩盖了外部名称。然而,在C#中不管嵌套级别如何
都不能在第一个名称的有效范围内声明另一个同名的本地变量。
const Type Identifier=Value
const 一个变量可以是null的引用变量,但它不能是某对象的引用,因为对象的
引用是在运行时决定的
要在方法的声明和调用上都使用ref关键字
参数数组
void ListInts(params int[] inVals){
…
}
第90页的方法重载
返回类型不同不是方法重载,形参的名称不同也不是方法重载
看到100页
public static都是修饰符,顺序任意
public staic int MaxVal;
static pubic int MaxVal;
【特性】【修饰符】核心声明
字段的类型与方法的返回类型不是修饰符,它们是核心声明的一部分
访问静态类成员,静态成员也可以 使用点运算符从类的外部访问,但因为 没有
实例所以必须使用类名 如D.Member=5; D是类名
没有类实例的静态成员仍然可以 被赋值,因为 字段与类有关,而与实例无关
静态成员即使没有 类的实例也存在。如果 静态字段有初始化语句那么 在使用该类的任何静态成员
之前初始该字段 但没必要 在程序 执行的开始 就初始化
如同静态字段 静态函数独立于任何类实例即使没有 类的实例,仍然可以 调用静态方法
public const double PI=3.1416;
没有实例化也能使用,表现的跟静态量一样,但是实际是在堆中没有存储位置的
它相当于#define PI 3.1416
属性就是定义了get set方法的类方法,只是set的get的那个值不是它提供的
它们不能直接被调用
静态构造函数
通常,静态构造函数初始化类的静态字段
- 初始化类级别的项
- 在引用任何静态成员之前
- 在创建 类的任何实例之前
- 类只能夺一个静态构造函数,而且不能带参数
- 静态构造函数不能有访问修饰符
120页
初始化发生在构造方法执行之后,因为在构造方法中设置的值可能会在之后对象初始
化中重置为相同或不同的值
对于一个名称为Point的类,它有两个公有整形字段X和Y,可以这样创建一个对象
new Point {X=5,Y=6};
X,Y必须是public的
this的目的:
- 用于区分类的成员和本地变量或参数
- 作为调用方法的实参
声明索引器
- 索引没有名称,在名称的位置的关键字是this
- 参数列表在方括号中间
- 参数列表中必须至少声明一个参数
ReturnType this [Type parame1,]{
get {}
set {}
}
索引器重载
只要索引器的参数列表不同,类就可以有任意多个索引器
请记住,类中重载的索引器必须有不同的参数列表
分部类:partial
我日,就是个分割文件的方法,在c++中就是extern
只支持单继承
要让编译器知道我们在故意屏蔽继承的成员,需要使用new修饰符,否则,会有烦人的警告
重载基类的方法需要函数名和参数表都相同才能重载去屏蔽基类的方法
被屏蔽的基类方法能用base.*来访问
- 尼玛,能把对子类的引用强转成对基类的引用
- 但是基类的引用只能使用基类的方法
- 子类引用使用全部的方法,如果有跟基类相同的方法,则覆盖
- 可以让基类的引用也能调用子类的相同方法,基类使用virtual,子类使用override
在构造函数中调用虚方法是有风险的,在执行基类的构造函数时,基类的虚方法会调用派生类的覆写方法,
但这是在执行派生类的构造函数方法之前,因此,调用会在派生类没有 完全 初始化之前传递到派生类
标记为public的类可以被系统内任何程序集中的代码访问,要使一个类其他程序集可见,使用public访问修饰符
标记为internal的类只能被它自己所在的程序集内的类看到,程序集即不是程序也不是DLL
对类的可访问性,只有两种修饰符,internal和public
成员不能不比它的类有更高的可访问性,比如一个类的可访问性限于它所在的程序集,那么类的成员个体也不能从程序集的外部
看到,无论它们的访问修饰符是什么,public也不例外
- 公有类的公有成员对同一程序集或其他程序集的所有类可见
- 任何类的私有成员只对它自己的类或嵌套类的成员可见
- 公有类的受保护成员对它自己的类成员或派生类的成员是可见的,派生类甚至可以在其他程序集中
扩展方法
-
想要添加一个新的功能,如果 有源代码,可以直接在这个类中添加 一个新方法 。
-
然而,如果不能修改这个类(如这个类在一个第三方类库中),那么只要它不是密封的,你就能把它用作一个基类
并在派生自它它的类中实现这个额外的方法。
- 最后,如果不能访问代码,或该类是密封的,或其他的设计原因使这些方法不能工作,就不得不在另一个类中
使用该类的公有成员编写一个方法。
第163页
要求:
- 声明扩展的类必须声明为static
- 扩展方法本身必须声明为static
- 扩展方法必须包含关键字this作为它的第一个参数类型,并在后面跟着它所扩展的类的名称
牛逼!!!
无后缀的实数字面量是double类型,不是float类型!
常规的字符串在双引号中要转义,在前面加个@就是在双引号中是什么样
的就按什么样的输出,除了双引号,要在@" aaa is " " bbbb " " "; 看到没,两个双引号才代表一个双引号
与C和C++不同,在C#中数字不具有布尔意义
引用的比较是浅比较,两个引用指向同一对象 ,就是true,否则即使内存中两具分离的对象在所有的其他方面
都完全相等也返回false
string也是引用,但它是深比较,委托也是引用,也是深比较。
&& 是会短路的
比如,bool bVal;int iVal=10;
bVal=(12)&&(9iVal++); 结果bVal=False,iVal=10;因为后面的iVal++没有计算,在&&处因为第一个是False就短路了
c#中的位操作
byte x=12,y=10; 用& | !^之类的来操作
隐式转换:
public static implicit operator TargetType(SourceTyppe Identifier){return ObjectOfTargetType;}
显式转换:
public static explicit operator TargetType(SourceType Identifier){return ObjectOfTargetType;}
运算符重载,感觉万年用不到啊,第189页
第191页的typeof挺有用的,可以得知类的方法和字段
using System.Reflection;
class SomeClass{
public int Field;
public void Method(){}
}
class Program{
static void Main(){
Type t=typeof(SomeClass);
FieldInfo [] fi=t.GetFields();
MethodInfo[] mi=t.GetMethods();
foreach (FieldInfo f in fi)
Console.WriteLine(“Field:{0}”,f.Name);
foreach (MethodInfo m in mi){
Console.WriteLine(“Method {0}”,m.Name);
}
SomeClass s=new SomeClass();
Console.WriteLine(“Type s:{}”,s.GetType(().Name);
}
}
using就是个try catch但是隐式的添加了finally,在出错时会dispose。
看到214页
结构
- 类是引用类型而结构是值类型
- 结构是隐式密封的,这意味着它们不能被派生
结构可以有构造函数和静态构造函数,但不允许有析构函数
使用结构的构造函数,要使用new运算符,即使不从堆中分配内存也要使用new运算符
这样做是为了初始化成员数据,不必再一个一个赋值
如果把一个结构用作ref或out参数,传入方法的是该结构的一个引用,这样就可以 修改其数据成员
{Flags} //这是专门为位操作做的,在使用一些ToString的方法时,不会被编译器误解
enum CardDeckSettings:uint
{
SingleDeck=0x01, //位0
LargePictures=0x02, //位1
FancyNumbers=0x04, //位2
Animation=0x08 //位3
}//按位与,按位或喽
Enum.GetNames(typeof(TrafficLight));//返回枚举中所有成员的全部名称
Enum.GetName(typeof(TrafficLIght),1);//返回响应枚举成员的名称
C#不支持动态数组
交错数组是什么鬼?
数组是引用类型
数组声明
int [,] firstArray; //三维整型数组
int[,] arr1; //二维整形数组
long[,] arr3; //三维long数组
long [3,2,6] SecondArray; //编译错误
数组的实例化
int [] arr2=new int[4]; //实例化四个元素
MyClass[] mcArr=new MyClass[4]; //实例化四个引用
int [,] arr3=new int [3,6,2]; //三维数组的实例化
二维数组跟C的样子很不同
int [,] intArr2 =net int[5,10]; //声明二维数组
intArr2[2,3]=7; ///向数组写入值
int var=intArr2[2,3]; //向数组读取值
显示初始化二维数组跟C很不同
int[] intArr =new int[] {10,20,30,40}; //没有等号的
int [,] intArray2=new int[,] { {10,1}, {2,10}, {11,9} } ; //实例化了三行两列的矩形数组
等价实例化
int [] arr=new int[2] {10,20};
int [] arr ={10,20};
隐式类型数组
int [] arr = new int [] {10,20};
var arr = new []{10,20};
string[] sarr=new string[] {“life”,“liberty”};
var sarr=new [] {“life”,“liberty”};
交错数组P242页
int [] [] arr =new int[3] []; //实例化顶层数组
int [] [,] arr =new int [3] [,]; //实例化带有3个二维数组的交错数组
数组协变P249页
- 值类型数组没有协变
数组Clone方法返回object类型的引用,它必须被强制转换成数组类型
int [] arr={1,2,3};
int [] arr2=( int [] )arr.Clone();
委托可以看成是函数指针 委托是引用类型
- 委托会调用其调用列表中的所有方法
- 调用列表中的方法可以是实例方法也可以是静态方法
- 委托保存的方法可以来自任何类或结构,只要他们在下面两点匹配
- 委托的返回类型
- 委托的签名 :包括ref和out修饰符
- delegate void MyDel(int x); //声明委托类型
- 如果一个方法在调用列表中出现多次,当委托被调用时,每次在列表中遇到这个方法时它都会被调用一次
调用带返回值的委托
- 调用列表中最后一个方法返回的值就是委托调用返回的值
- 调用列表中所有其他方法的返回值都会被忽略
调用带引用参数的委托
- 在调用列表中的下一个方法时,参数的新值会传给下一个方法。
delegate double MyDel(int par);
MyDel del=delegate(int x ) {return x+1;}; //委托的匿名方法
MyDel le1=( int x ) => {return x+1;}; //Lanbda表达式 P269页
使用系统的委托创建事件 P278页
事件在第14章
接口是引用类型
- 按照惯例,接口名称必须从大写 的I开始 比如 :ISaveable
- 关键字interface
- 接口允许是public private protected的,而接口成员不允许加访问修饰符
- class Derived: MyBaseClass,IIfct1,IEnumerable,IComparable
- 只能有一个基类且只能在接口的最前面
接口和as运算符
- 强制转换对象来获取接口的引用
- ILive b_interface =a_class as ILive;
- if (b_interface==null){
- Console.WriteLine(“Baby is called:{0}”,b_interface.BabyCalled());
- }
限定接口名称指的是显示指定某个接口的实现P297页
转换
- 强制类型转换 (sbyte) varl; //(目标类型) 源表达式;
- 预定义的转换类型:
- 装箱:将任何值类型转换成
- object 类型
- System.ValueType类型
- 拆箱
- 可以将一个装箱的值转换为原始类型
- 装箱:将任何值类型转换成
溢出检测上下文
- checked(表达式)
- unchecked(表达式)
- checked
- {
- byte sb;
- ushort sh=2000;
- sb=(byte)sh;
- Console.WriteLIne(“sb:{0}”,sb);
- }
各种强制转换在C#中的表现P310
引用转换
- 引用对象由内存中的两部分组成,引用和数据
- 由引用保存的那部分信息是它指向的数据 类型
- 引用转换接受源引用返回一个指向堆中同一位置的引用,但是把引用标记为其他类型
隐式引用转换P313
- 所有引用类型可以被隐式转换为object类型
- 任何类型可以隐式转换到它继承的接口
- 类可以隐式转换到:
- 它继承链中的任何类
- 它可实的任何接口
装箱转换
- 装箱是隐式转换
- 装箱是创建副本
- 将值类型赋值给引用类型
拆箱转换
- 是把装箱后的对象转换回值类型的过程
- 拆箱是显示转换
用户自定义转换
-
implicit(隐式) 和 explicit(显示)是必须的
-
public static 是必须的
-
public static implicit operator TargetType(SourceType Identifier)
-
{
- return ObJECToFTargetType;
-
}
-
public static implicit operator int (Persion p)
-
{
- return p.Age;
-
}
is 运算符
- 使用is运算符检查转换是否会成功完成
- is运算符只可以用于引用转换以及装箱 拆箱转换不能用于用户自定义转换。
as运算符
- as运算符和强制转换运算符类似,只是它不抛出异常,转换失败返回null
- as只能用于引用转换和装箱转换,它不能用于用户自定义转换或到值类型的转换
泛型类型
-
类
-
结构
-
接口
-
委托
-
- class SomeClass <T1,T2> - { - public T1 someVar=new T1(); - public T2 otherVar=new T2(); - }
-
T1,T2称为类型参数,在类声明上使用<>来区分普通类的声明
Where子句
-
where 类型参数:约束1,约束2…
-
class MyClass<T1,T2,T3> where T2:Customer where T3:IComparable { ........ }
-
最多只有一个排在第一位置的主约束
-
可以有任意多的接口名约束
-
如果存在构造函数约束,放在最后。
看到345页
out是协变
in是逆变
- 一堆骚操作,为什么要把派生类转成基类啊,那派生来做甚
356页自己为类实现foreach
361页便捷生成foreach
364页我们让类中的字段可枚举是常用的吧,可能。
- 迭代器的命名空间 System.Collections.Generic
LINQ
-
using System.Linq
-
读作link,代表语言表集成查询
-
它允许我们以使用SQL查询数据库的方式来查询数据集合
-
使用LINQ,你可以从数据库/程序对象的集合/XML文档中查询数据
-
//int[] numbers = { 2, 13, 5, 6, 8 }; //IEnumerable<int> lowNums = // from n in numbers // where n < 10 // select n; //foreach (var x in lowNums) //{ // Console.Write("{0},", x); //} //Console.Read(); //var student = new { Name = "aaa", age = 19 }; //Console.WriteLine(student); //var groupA = new[] { 3, 3,4, 5, 6 }; //var groupB = new[] { 6, 7, 8, 9 }; //var someInts = from a in groupA // from b in groupB // let sum = a + b // where sum==12 // select new { a, b, sum }; //foreach (var a in someInts) //{ // Console.WriteLine(a); //}
388页就是C#让人喜欢的地方吧,很多标准查询运算符。
XML类
- using System.Xml.Linq
- IEnumerable commets=xd.Nodes().ofType();
- 指定返回某个类型的节点,上面Nodes是返回所有类型的节点,现在用XComment指定返回这个类型的节点
用XML类的来改峰哥的代码吧,什么切
来切去的好乱啊
这有个计时的挺有用的 Stopwatch
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using System.Diagnostics;
class MyDownloadString
{
Stopwatch sw = new Stopwatch();
public void DoRun()
{
const int LargeNumber = 60000000;
sw.Start();
int t1 = CountCharacters(1, "http://microsoft.com");
int t2 = CountCharacters(2, "http://www.illustratedcsharp.com");
CountToALargeNumber(1, LargeNumber);
CountToALargeNumber(2, LargeNumber);
CountToALargeNumber(3, LargeNumber);
CountToALargeNumber(4, LargeNumber);
Console.WriteLine(" Chars in http://microsoft.com :{0}", t1);
Console.WriteLine(" Chars in http://www.illustratedcsharp.com :{0}", t2);
}
private int CountCharacters(int id,string uriString)
{
WebClient wc1 = new WebClient();
Console.WriteLine(" Starting call {0} :{1,4}ms", id, sw.Elapsed.TotalMilliseconds);
string result = wc1.DownloadString(new Uri(uriString));
Console.WriteLine(" call {0} completed :{1,4}ms", id, sw.Elapsed.TotalMilliseconds);
return result.Length;
}
private void CountToALargeNumber(int id,int value)
{
for (long i = 0; i < value; i++) ;
Console.WriteLine(" End counting{0}:{1,4} ms", id, sw.Elapsed.TotalMilliseconds);
}
}
namespace start
{
class Program
{
static void Main(string[] args)
{
MyDownloadString ds = new MyDownloadString();
ds.DoRun();
Console.Read();
}
}
}
异步编程
- 三种返回类型
- void
- Task
- Task
- 方法头有async修饰符
- 方法中包含多个awit表达式
- 方法的参数不能为out /ref
定时取消异步任务
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
class MyClass
{
public async Task RunAsync(CancellationToken ct)
{
//一进来就判断,好习惯呀,目前用不到
//if (ct.IsCancellationRequested)
//{
// Console.WriteLine("执行到这里返回");
// return;
//}
await Task.Run(() => CycleMethod(ct));
}
void CycleMethod(CancellationToken ct)
{
Console.WriteLine("Starting CycleMethod");
const int max = 5;
for (int i = 0; i < max; i++)
{
if (ct.IsCancellationRequested)
return;
Thread.Sleep(1000);
Console.WriteLine("{0} of {1} iterations completed!", i + 1, max);
}
}
}
namespace cancelTask
{
class Program
{
static void Main(string[] args)
{
CancellationTokenSource cts = new CancellationTokenSource();
CancellationToken token = cts.Token;
MyClass mc = new MyClass();
Task t = mc.RunAsync(token);
//Thread.Sleep(3000);//等待3秒
//cts.Cancel();//取消操作
t.Wait();
Console.WriteLine("Was Cancelled:{0}", token.IsCancellationRequested);
Console.Read();
}
}
}
同步等待任务
- Task.WaitAll 同步等所有任务
- Task.WaitAny 同步等任意一个任务
429页在异步方法中异步地等待任务
- Task.WhenAll 异步等所有任务
- Task.WhenAny 异步等做任意一个
Task.Delay不会阻塞线程,线程可以继续其他工作
Task.Yield
- 创建一个立即返回的awaitable,可以理解成离开当前的消息队列,回到队列末尾,让处理器有时间处理其他任务
- Yield方法在GUI程序中非常有用,可以中断大量工作,让其他任务使用处理器。
BackgroundWorker类 442页
-
using System.ComponentModel;
-
void DoWorkEventHandler(object sender,DoWorkEventArgs e)
- DoWirkEventArgs
- Argument
- Result
- Cancel
- DoWirkEventArgs
-
void ProgressChangedEventHandler(object sender,ProgressChangedEventArgs e)
- ProgressChangedEventArgs
- ProgressPercentage
- UserState
- ProgressChangedEventArgs
-
void RunWorkerCompletedEventHandler (object sender,RunWkrkerCompletedEventArgs e)
- RunWorkerCompleteEventArgs
- Cancelled
- Error
- Result
- UserState
- RunWorkerCompleteEventArgs
并行循环
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ParallelLoopPractice_For
{
class Program
{
static void Main(string[] args)
{
//并行方式输出平方
//Parallel.For(0, 15, i => Console.WriteLine("The square of {0} is {1}", i, i * i));
//Console.Read();
const int maxValues = 50;
int[] squares = new int[maxValues];
//并行方式填充数组
Parallel.For(0, maxValues, i => squares[i] = i * i);
foreach(var i in squares)
{
Console.WriteLine(i);
}
Console.Read();
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace ParallelLoopPractice_ForEach
{
class Program
{
static void Main(string[] args)
{
string[] squares = new string[] { "we", "hold", "these", "truths", "to", "be", "self-evident", "that", "all", "men", "are", "created", "equal" };
Parallel.ForEach(squares, i => Console.WriteLine(string.Format("{0} has {1} letters", i, i.Length)));
Console.Read();
}
}
}
看到447
异步等待
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace async_wait
{
delegate long MyDel(int first, int second);
class Program
{
static long Sum(int x,int y)
{
Console.WriteLine(" Inside sum");
Thread.Sleep(200);
return x + y;
}
static void Main(string[] args)
{
MyDel del = new MyDel(Sum);
Console.WriteLine("Before BeginInvoke");
IAsyncResult iar = del.BeginInvoke(3, 5, null, null);
Console.WriteLine("after BeginInvoke");
Console.WriteLine("Doing stuff");
long result = del.EndInvoke(iar);
Console.WriteLine("after EndInvoke:{0}", result);
Console.Read();
}
}
}
异步轮询
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace async_look
{
delegate long MyDel(int firtst, int second);
class Program
{
static long Sum(int x, int y)
{
Console.WriteLine(" Inside Sum");
Thread.Sleep(100);
return x + y;
}
static void Main(string[] args)
{
MyDel del = new MyDel(Sum);
IAsyncResult iar = del.BeginInvoke(3, 5, null, null);
Console.WriteLine("after BeginInvoke");
while (!iar.IsCompleted)
{
Console.WriteLine("Not Done");
for (long i = 0; i < 1000000; i++) ;
}
Console.WriteLine("Done");
long result = del.EndInvoke(iar);
Console.WriteLine("Result:{0}", result);
Console.Read();
}
}
}
异步回调
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.Remoting.Messaging;
using System.Threading;
namespace async_call
{
delegate long MyDel(int first, int second);
class Program
{
static long Sum(int x, int y)
{
Console.WriteLine(" Inside Sum");
Thread.Sleep(100);
return x + y;
}
static void CallWhenDone(IAsyncResult iar)
{
Console.WriteLine(" Inside CallWhenDone.");
//获取类对象的引用
AsyncResult ar = (AsyncResult)iar;
//获取委托的引用
MyDel del = (MyDel)ar.AsyncDelegate;
long result = del.EndInvoke(iar);
Console.WriteLine(" The result is:{0}.", result);
}
static void Main(string[] args)
{
MyDel del = new MyDel(Sum);
Console.WriteLine("Before BeginInvoke");
IAsyncResult iar = del.BeginInvoke(3, 5, new AsyncCallback(CallWhenDone), null);
Console.WriteLine("Doing more work in Main.");
//Thread.Sleep(500);
Console.WriteLine("Done with Main .Exiting.");
Console.Read();
}
}
}
定时器
- dueTime 是回调方法首次被调用之前的时间,如果dueTime被设置为特殊的值Timeout.Infinite,则计时器不会开始。如果被设置为0,回调函数会被立即调用
- period是两次成功调用回调函数之间的时间间隔。如果它的值设置为Timeout.Infinite,回调在首次被调用之后不会再被调用。
- state可以是null或在每次回调方法执行时要传入的对象的引用。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
namespace async_Timer
{
class Program
{
int TimeCalled = 0;
void Display(object state)
{
Console.WriteLine("{0} {1}", (string)state, ++TimeCalled);
}
static void Main(string[] args)
{
Program p = new Program();
Timer myTimer = new Timer(p.Display, "Processing timer event", 2000/*2s后第一次调用*/, 2000/*每2s重复一次*/);
Console.WriteLine("Timer started.");
Console.Read();
}
}
}
- System.Window.Forms.Timer
- 定期把WM_TIMER消息放到程序的消息队列中。当程序从队列获取消息后,它会在主用户接口线程中同步处理,这对Windows应用程序来说非常重要
- System.Timers.Timer
- 有一个叫做Elapsed的成员事件,每次时间到期就会发起这个事件,这个计时器可以运行在用户接口线程或工作者线程上。
看到456页
后面的不看了。这就叫速成。