forked from shangerxin/BookNotes
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathAccelerated C# 2008=Trey nash; Note = Shang.txt
379 lines (322 loc) · 23.1 KB
/
Accelerated C# 2008=Trey nash; Note = Shang.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
程序集合, 可以使用Appdomain.Load()手动加载
类
public class ClassName:Base
{
}
抽象类,嵌套类,静态类
类的构造,可以使用同一个类中的静态方法的返回值来构造本类对象的属性值。
静态构造函数,一个类最多只能有一个静态构造函数,它不接受任何阐述,并且不能直接调用,是自动调用的,在类的实例第一次被创建或调用类的任何其他静态函数之前调用。用base关键字来调用基类元素
封闭类,sealed,可以单一封闭方法,也可以封闭整个类
接口
public interface InterFaceName:Base
{
}
索引器,是静态的,始终是基于实例的。声明中,修饰符后面跟着索引器的类型,调用后将返回这个对象给调用者。很像属性和方法的混合物。
pubilc returnType this[ parameterIndex ]
{
get
{
return objReturnType[parameterIndex];
}
}
C#中的保留字,对于程序中定义的变量名是否与中间语言的变量名冲突,可以使ildasm之类的工具来查看。位置在.net sdk\bin\ildasm.exe
匿名类型
var varName = new { value }; //类似于,matlab的元包数组
对相初始化器,类似于vb的with
classType = new objClass
{
propertyName0 = value0;
propertyName1 = value1;
}
装箱和拆箱,就是使用object对象来作为任何对象索引,同C++中的父级指针
object o = anyVarTypeObj;
anyVarTypeObj = ( anyVarTypeObj ) o;
装箱的效率较低,实际使用时候要尽量避免
排序接口,System.IComparable
终结器,Finalize方法是System.Object中一个虚方法的重写。C#总不能显示调用该方法,使用析构函数来完成,其中的对象需要线程安全,不能显示调用
实现IDisposable接口,终结器的使用该方法完成清理,可以从写该函数,代码中可以显示调用,也可以用终结器自动调用
使用using关键字,在之后的圆括号里获得资源,在之后的大括号中使用资源,会自动释放有限资源
using( new resource, ... )
{
}
参数传递加ref为引用传递,调用方法时参数需要明确赋值; out引用传递,指定的参数不需要显示赋值
参数数组
returnType FunctionName( params type[] varArrayName )
抽象方法与虚方法
abstract 必须重写, virtual 可以重写。C#使用虚方法与接口来实现多态性
派生类中重写方法必须使用override 修饰符来标记方法
修改类中的方法,就应该重写基类的方法,并把它标记为virtual,要慎用继承防止增加代码的耦合度
WSDL, Web Services Description Language
接口,可以声明方法、属性、事件和索引器,但不能声明静态方法
public interface IMyInterfaceName( [parameters] )
{
returnType FunctionName( [parameters] ); //隐含为共有的
}
多个接口中可能拥有相同的方法名,这时使用new 关键字来隐藏其他接口中的方法,在实现接口的类中调用某个接口的指定方法时可以采用强制转换的方法
((InterfaceType0)objClass).FunctionName();
((InterfaceType1)objClass).FunctionName(); //objClass同时实现了InterfaceType0, InterfaceType1两个接口,切这两个接口中的方法同名
接口的实现分为隐式接口实现,显示接口实现
//定义接口
public interface IUIControl
{
void Paint();
}
public class StaticText:IUIControl
{
void Paint(); //无法通过编译,未实现接口
public void Paint(); //可以通过编译,隐式实现借口
public void Paint()
{
//code 显示实现接口
}
}
派生类中覆盖接口实现
对于基类中已经实现接口的方法重新定义时需要加new 关键字
public new void FunctionName()
使用值类型的接口的时候,例如转换函数,最好使用convert类来进行,不要使用强制转换来调用类型转换
接口名字扩充的定义设计准则是在需要扩充的接口名字后面加数字序号来完成,而com中使用名字后面加ex来定义
与接口匹配的称为契约,即抽象类,用抽象方法来规定继承该类的方法必须实现的函数,针对某种特殊功能的函数,只有不分类才具有时只定义接口,称为接口契约
不要修改已经发布的接口声明
微软基础类MFC, Microsoft Foundation Classe
重载操作符
public static Complex operator + ( type obj0, type obj1 );
重载时应当保持原有习惯,不改变操作数的个数等
可重载的操作符有
一元的 +, -, !, ~, ++, --, true, false;
二元的 +, -, *, /, %, &, |, ^, <<, >>, <, >, >=, <=;
类型转换操作符重载, 使用关键字,并且建议开启/checked+选项防止内存泄漏
public type operator double( parameter );
异常处理
手动抛出异常, throw new ExceptionType(); / throw objException;
异常处理过滤器,通过注册一个AppDomain.UnhandledException委托来安装一个未处理一场的过滤器,如果程序中有未处理的异常发生,那么就会自动调用委托函数,并将一场作为UnhandledExceptionEventArgs的一个实例
.net 1.1会吞掉所有未处理一场,而.net2.0之后会除了AppDomainUnloadException和ThreadAbortException之外的一场发生并未处理时终止当前线程或进程
try{} catch( typeException ){} ...catch{}... finally{}
不带类型的catch在try中只能有一个这样的通用异常处理块,并且不能检测异常类型,针对处理某些非C#语言编写的异常列表
重新抛出已捕获的异常,使用不带参数的throw语句来完成
catch( exceptionType )
{
//handle code
throw; //将会重新抛出捕获的exceptionType对象
}
尽量不要再finally块中抛出异常;要尽量不再终结器中抛出异常,这样会导致进程中止,因为C#中的终结器并不是最后的析够函数
异常中立的代码,没有异常处理的代码,但在异常发生时同样可以正常工作
受限制区域constrained execution region CER,.net2.0之后针对clr的异步一场threadAbortException, outOfMemoryException, StackOverflowException还可能发生。保证代码在执行前的运行环境安全,可以使用System.Runtime.CompilerServices命名空间里的RunTimeHelpers类的PerpareContrainedRegions()方法来实现CER
RuntimeHelpers.PrepareConstrainedRegions();
try{}
finally
{
functionName();
}
[ReliabilityContract( Consistency.WillNotCorruptState, Cer.Success)]
void functionName()
{
}
对于clr保证了当前线程在cer中不会注入异步异常,但其他类异常并不再控制范围,如OutOfMemoryException
临界终结器和SafeHandle, 很少创建临界终结器对象,而是通过派生自 SafeHandle获得想要得行为,通过P/Invoke或Com。在创建SafeHandle时检查是否已经有现成的派生类可用,如通过P/Invoke来调用Win32 DeviceIoControl功能来直接和设备驱动打交道的代码,这是SafeHandle就足够保存设备上直接打开的句柄。针对非托管代码来设计的
调用系统蓝牙发射器,win32 api BluetoothFindFirstRadio
介绍代码安全可靠运行的书Keep Your code Running with the Reliability Features of the .Net Framew = Stephen Toub
自定义异常,派生至System.Exception类, 并在类头加入[Serializable()]方法
using System;
using System.Runtime.Serialization;
[Serializable()]
public class EmployeeException: Exception
{
public enum Cause
{
//原因
}
public EmployeeException(0){}
public EmployeeException(1){} //多种构造函数
private Cause reason;
public Cause Reason
{
get{ return reason; }
}
}
C#中的资源释放并不是和C++中一样,使用完之后立即回收,需要等待垃圾回收器回收,而这时有可能导致资源使用上的冲突,C#中解决这种冲突的方法是使得类实现IDisposable接口的Disposeable方法,并在使用完敏感资源之后立即调用Disposeable方法来释放有限资源,并且在代码中使用using块来保证使用后准确调用Disposeable方法,并提高代码的可读性,避免了使用过多的try...catch语句
实现了IFormattable接口来实现格式化
ICustomFormatter接口允许一个对象替换或者扩展一个内质类型或者一个已经存在的IFormattable接口
实现全球化,转换语言区域,引用using System.Globalization; 使用CultureAndRegionInfoBuilder类,调用的程序集合为sysglobl.dll
本地数据标记语言 Locale data Markup Language, LDML
字符串的格式化String.Format
高效构造复合字符串 System.Text.StringBuilder.StringBuilder类,针对
string compound = "Vote" + " for " + "Pedro"
使用 objStringBuilder.Append(strToAdd).Append(strToAdd);
使用正则表达式regex.Match(), 如查找,替换文本等, System.Text.RegularExpression
数组、容器与迭代器,定义使用java风格
隐式类型化数组
int[] aryName = new [] {4,3,2};
var aryName = new[]{3,32,1};
int[,] aryName = new int[5,3];
int[,] aryName = new{{},{}};
对于派生类可以用父类对象数组存储派生类对象,C#中可以这样操作;但在C++中不能这样存储,这样会导致派生类被截取,只保留与父类相同的部分。
排序,只要数组的元素实现了IComparable接口,不能对多位数组排序。实现System.Array
同步,实现ICollection接口;管理同步最简单的方法是借助System.Monitor类,需要使用C#的clock关键字,该类允许在一个对象上获得内建的同步锁,可以从ICollection.SyncRoot对象之上来获取锁
Array.Length返回数组的总和,每个维度的长度可以由Array.GetLength()方法获得
锯齿数组,可以定义每一个维度不同的数组
容器类型,System.Collections, System.Collections.Specialized命名空间中。容器类型的更本类型是ICollection<T>
自定义容器的时候需要从System.Collections.ObjectModel命名空间中的Collection<T> 继承而来
列表,IList<T>实现索引服务
字典,IDictionary<TKey, TValue>类型作为一个泛型和IDictionary强类型化
当暴露容器的时候,应该考虑创建一个类型的子类,即使只是提供一个描述容器的更丰富的名称和一个方便访问的可扩展点
如果想要创建一个只读的集合,则需要使用ReadOnlyCollection<T>来封装实现了IList<T>的任何类型
集合类的效率,当使用多个foreach语句迭代的时候List<T>明显快于ArrayList,可以付诸实用性能分析器来检测代码的效率
遍历,实现IEnumerable<T>接口或IEnumerable接口
使用yield关键字,reset方法不适用于yield块生成的枚举器,在枚举结尾的时候任何yield块内部的finally块都如期执行,C#的lock语句在底层的运行机制上归结为try/finally结构;yield块的方法上声名ref或out参数是无效的,由于yield捕获本地变量和参数; 一个类尽可能不要同时实现IEnumberable<T>和IEnumberator<T>两个接口,因为一个单一类型要么是容器要么是枚举器不能两种都是
CAS, code access security;相关的阅读 .net framework security = Brian A. LaMacchia
委托delegate,匿名方法用于简化委托的语法
创建委托
public delegate type ProcessResults( [parameters] ); //parameters用于付给被委托的函数;C#编译时会定义一个从MulticastDelegae中派生的类型,该类型也实现了一个invoke的方法,编译器四不允许直接调用委托的inovke方法的。函数的签名要与委托的签名相兼容,类型和返回类型一致或者可以隐式转换在全局定义,即是一种同类的函数指针数组,使用格式
ProcessResults objPR = new ProcessResult( FunctionName ); //赋予指定函数名
objPR( [parameters] ); //进行调用
委托链的返回是最后一个委托值;任何一个委托函数抛出异常,委托链就结束;如果传递的参数是引用类型的,任何一个委托函数修改了参数,之后的函数均有体现;委托链必须显示转换委托类型才能保证正常调用
ProcessResults[] delegates = new ProcessResults[]
{
new ProcessResults( function0 );
new ProcessResults( function1 );
}
Processresults chained = (ProcessResults) Delegate.Combine( delegates );
实现将委托数组转换为委托链,也可以从委托链中提取所有委托,来避免一个委托函数出错导致其他的委托均不能继续工作
事件,发布、订阅模式;
使用制定义事件需要,建立事件,事件发布者,事件监听者
public event EventHandler<T> objEvent; //建立事件处理, T必须为System.EventArgs类中派生的类型
匿名方法,在建立委托对象时
objProcessResults = delegate( [parameters] ){ //code };
泛型,创建开放的类型,如同C++中的模板类,例如,T就是类型参数
public class MyCollection<T>
{
public MyCollection()
{
}
private T[] storage;
}
使用泛型
MyCollection<int> myType; //建立一种新的类型myType
myType objmyType;
泛型的定义可以嵌套,定义多层次的嵌套泛型,如果泛型具有静态初始化器,那么在每次定义新的该泛型的生成类型的时候,都会调用该初始化器;在其中创建较大的数据结构的时候容易引起隐藏的内存消耗
但区别于C++的是,C#中泛型是动态的,可以动态增减方法及属性,但C++中不能动态修改模板类, 并且不能打包到dll中,例如微软的stl模版库的实现均在dll中
泛型接口,同泛型类是一样的;泛型方法;泛型委托;泛型转换
默认表达式, varName = default(T); 来初始化一个对象或者一个参数化类型的实例
定义一个变量是否可以是null类型的值
public Nullable<T> varName;
public type? varName;
两种方法是等效的
使用泛型来定义变量类型的时候相同类型参数的泛型类型要统一
约束,约束泛型的所有生成对象必须实现某个接口,或一个类
public class className<T> where T: ClassNameOrInterfaceName
{
}
约束泛型的类型参数必须 实现ClassNameOrInterfaceName,多个类型参数的时候可以使用多个where子句
泛型容器,可以参阅Sysem.Collections.Generic命名空间的文档,其中包含Dictionary<TKey, TValue>、LinkedList<T>、 List<T>、Queue<T>、SortedDictionary<TKey, TValue>、SortedList<T>、HashSet<T>和Stack<T>,泛型接口比一般接口有更好的类型安全性
在使用泛型的时候要注意操作符号的重载,有的时候由于类型参数的约束不一定支持所有的运算符号,所以要进行一些类型转换,如使用convert类或重载操作符等
ATL,Active Template Library, 活动模板库; 相关的书 Modern C++ Design: Generic Programming and Design Patterns Applied = Andrei Alexandrescu
在set方法中,默认输入的参数为value,可以使用其来复赋值
线程,System.Threading类, objThread.start, objThread.join等方法
后台线程,当建立后台线程的时候一定要保证如果线程非正常退出的时候保存了所有的需要存储的数据;
多线程,多个线程的程序中线程的执行顺序是无法保证的,除非采用同步机制
[ThreadStatic] //线程静态变量声明
托管对象与非托管线程及COM套件,对于托管线程调用了非托管COM组件时,在执行到非托管代码区域调用Thread.Abort, Thread.Interrupt等方法的托管方法无效
线程同步,尽量使用轻量级同步,使用类似Monitor锁类的方法,尽量不要使用Mutex互斥锁,因为Mutex是进程间的的锁,尽量将所有的操作控制在用户级别,不要在用户级与内核级之间转换。例如Mutex, Semaphore, EventWaitHandle等都是内核级类型等待
使用Interlocked实现轻量级同步,在多线程间保证一个变量或对象的通信连接
Interlocked.Increment( ref parameter ); Interlocked.Decrement( ref parameter );Interlocked.Exchange( ref parameter, parameter );
可以使用lock(parameter){}来保证多处理器间的数据锁定,用于SMP系统中
Monitor类允许了同一进程间的线程同步,而Mutex用于多进程间的数据同步
ReaderWriterLock锁对象,可以用于多线程同步的时候访问数据,实现允许重入的自旋锁;ReaderWriterLockSlim来增强防止死锁的能力
信号量,通过System.Threading.Semaphore类来支持信号量,用于允许一定数目的线程同时获得资源,达到最大资源后阻赛之后请求的线程
事件,可以使用两类信号事件,ManulResetEvent和 AutoResetEvent
同步对象和WaitHandle,使用时要在代码中加入using关键字
[DllImport( "KERNEL32.DLL", EntryPoint = "CreaeteEventW", SetLastError = true)]
private static extern SafeWaitHandle CreateEvent( IntPtr lpEventAttribtes, bool bManulReset, bool bManualReset, bool bInitialState, string lpName );
调用系统API来存储句柄,引入自定义事件
线程池,.net系统中自带ThreadPool类提供一个内置的,可以拿来就用的线程池;另一种方法是使用异步委托来使用线程池, System.MulticastDelegate类,其中定义了一个Invoke方法,并提供BeginInvoke, EndInvoke方法,这两个方法之间可以写一些处理其他事情的方法;线程池的另一个入口方法是使用 定时器类的委托Timer
转换字符串为utf8, Encoding.UTF8.GetBytes( string );
SMP, 多处理器平台
规范形式 canonical form, checklist检查单;相关书籍, <设计模式:可复用面向对象软件的基础>,模版方法
在C++中类可以使用private virtual 关键标记元素,但在C#总不能使用,为了避免安全漏洞,并且在ref C++中也不可以使用该关键字,因为这类程序集合可以C#继承
对象的克隆,实现ICloneable接口 ,尽量避免使用克隆,因为接口并没有限制是深度复制还是潜复制,容易遗留安全隐患; 相关书籍 <.net 设计规范:.net 约定、惯用法与模式>。对象的清除,实现IDisposable接口
使用终结器的时候一定要小心,在实现的时候尽量不要引用外部对象,如果引用了也要确保引用的外部对象一定存在的
应用程序环境类Environment, 应用程序状态类AppDomain
对象的相等,Object.Equals虚方法,两个引用的相等,表示两个引用指向同一个内存区域,对于引用,如果区域不等也是不相等的; 值相等System.ValueType重写了Object.Equals方法,重写结构类的Equals方法比System.ValueType的反射机制重写效率要高。实现Equals方法的时候不应当抛出异常。
重写了Equals方法也应当从写GetHashCode方法,因为如果x.Equals(y) == true则
x.GetHashCode() == y.GetHashcode(), 生成的数可能不唯一,不允许抛出异常
对于数组等大型数据结构,getHashCode计算代价较高,每次都计算对象的CRC32值
排序,实现IComparable;显示,实现IFormattable; 实现对象类型转换,IConvertible,对于不支持的类型转换,应该抛出异常
C++中可以使用,const_cast关键字去除const关键字的作用, C#中加入readonly关键字Exceptional c++中文版,47个C++难题、编成问题和解决方案=Herb Sutter
模板函数编程,C++ Template Metaprogramming:Concepts, Tools, and Techniques from Boost and Beyond = David Abrahams
值类型派生至System.ValueType, 引用类型派生自 System.Object
扩展方法,可以修改任意类型的公共接口,静态类命名空间
public static class ExtensionMethods
{
static public void WriteLine( this String str )
{
//取代WriteLine函数
}
}
同样可以书写泛型方法
自定义,迭代器,使用扩展方式来重新完成这个工作
public static IEnumerable<T> GetRowMajorIterator<T>( this List<List<T>> matrix )
{
foreach( var row in matrix )
{
foreach( var item in row )
{
yield return item;
}
}
}
lambda表达式,使用更流畅的方法实现匿名函数和委托;具有两种形式
x => x /2
Func<>类型是.net 命名空间中提供的简单类型来声明委托,最多接受4个参数返回一个结果;
Func<int, double> expr = (double x) => x / 2; //使用lambda表达式来定义委托
expr( someNumber ); //调用委托
lambda语句块,(x, y) => {return x * y};
树形表达式,C#3.0中支持System.Linq.Expressions命名空间中的类型将lambda表达式转换为表达式树的能力
匿名递归,使用lambda表达式来完成递归函数的设计
Func<int, int> fact = null;
fact = (x) = x > 1 ? x * fact( x-1) : 1; //这样完成匿名递归是不安全的,因为在lamba表达式外部同样可以修改委托fact的委托函数内容,会导致递归的无法顺利完成,解决方法
delegate TResult Anonre<TArg, TResult>( AnonRec<Targ, Tresult> f, TArg arg );
Anonrec<int, int> fact = (f, x ) => x > 1? x * f( f, x-1 ) : 1;
关键在于将委托作为参数传递给递归
currying,闭包,缓存是闭包的一个强大的应用,使用扩展函数将以有的委托的阐述修改重新定义
public static Func<TArg1, TResult> Bind2nd<TArg1, TArg2, TResult>( this Func<TArg1, TArg2, TResult> func, TArg2 constant )
{
return (x) => func( x, constant );
}
LINQ语言集成查询,C#与C本身是命令形势的,而SQL是函数形势的,LINQ是桥梁,包含LINQ to Objects, LINQ to Sql, to Dataset, to Xml
System.Linq.Enumerable 提供了LINQ to Objects方法, 用于对内部集合类进行查询
System.Linq.Queryable 提供了LINQ to Sql 的方法,用于对数据库查询,LINQ对象的定义方法有两种一种是使用lambda表达式,一种是使用var 类型加搜索语句
Linq表达式的查询语言顺序与sql语句的习惯顺序相反,例如
var query = from employee in employees
where employee.Salary > 1000
orderby employee.LastName
select new { LastName = employee.LastName, FristName = employee.Firstname };
LINQ关键字, from, join, where, group, into, let, orderby, select
from 用于定义范围,一条查询语句可以有多条范围,实际上就是用于定义引用变量
var query = from x in Enumerable.Range(0, 10)
form y in Enumberble.Range(0, 10)
select new{ X = x, Y = y, product = x * y };
join 用于连接两个独立数据源中的数据,实际上就是用于定义联合查询变量
var query = from emp in employees
join n in empNationalities
orderby n.Nationnality descending
select new
{ Id = emp.Id, Name = emp.Name, Nationality = n.Nationality };
where 设置查询条件,如果是IEnumerable类型查询,那么就会调用Enumerable.where, 条件转换为lambda表达式, where 转换为函数
order by 用于对结果排序
group by 是可选的,在划分查询输入的时候非常有用,将输入映射为IGrouping接口的容器,同时还派生自IEnumerable 接口,用于划分查询结果
var query = from x in numbers group x by x % 2;
into 功能类似于let语句用于定义一个查询范围内有效的字符标记,将group或 join的结果赋予一个符号
var query = from x in numbers
group x by x % 2 into partition
where partition.Key == 0
select new
{ Key = partition.Key, Count = partition.Count, Group = partition };
take 用于从查询结果中取出指定数量的结果
var linkList = finiteList.where( x => x > 3 )
.select( x => x * 2 )
.Take( 10 ); //使用lambda表达式定义的LINQ对象finiteList
使用LINQ对象
foreach( var item in query )
{
//code
}