forked from shangerxin/BookNotes
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathC#程序设计语言=Anders Hejlsberg;Note=eshang.txt
643 lines (624 loc) · 24.4 KB
/
C#程序设计语言=Anders Hejlsberg;Note=eshang.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
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
C#程序设计语言=Anders Hejlsberg;Note=eshang
:介绍
>变量类型
值类型,引用类型
ref,out专门是为值类型设计的
>值类型
byte, sbyte, short, ushort, int, uint...
enum
struct
decemal, 不是真正的浮点小数,float,double是二进制的浮点数
可空类型,type?
>引用类型
object, string, class, interface, int[], delegate
>语句
check, unchecked
checked{i++;}
;
lock(this){}
;
using(resourceObject){}
;
yield ...;
;
foreach( type varname in serials){}
>类的成员
静态成员(static member), 属于类本身
实例成员(instance member)
>访问控制
public, 与C++中有很大区别,C#中等同于C++的extern全局访问
protected, 类及子类
internal, 程序集内部
protected internal, 最好不要这种组合,这个程序或类的子类可以访问
private, 默认
>类型参数
public class className<typeParameter, typeParameter1>:baseClassName
{
const int defaultCapacity = 4; //常量
public typeParameter varName;
public typeParameter1 varName1; //字段
public readonly static className<typeParameter> varName = somevalue; //静态字段属于类本身
void function(typeParameter m, ref type varName, out type varName, params objec[] varName){}; //方法
public abstract type functionName();
public override type baseClassFunction();
public int Capacity //属性
{
get;
set{value};
}
public T this[int index] //索引
{
get{return items[index]};
set{value};
}
public event EventHandller changed; //事件
public static bool operator ==(List<T> a, List<T> b) //操作符
{
}
}
readonly, 保护的是引用,不是引用的值,例如如果是数组,那么数组可以增减,但不能赋予其他数组的引用
ref, 代表引用参数
out, 作为输出参数
params, 方法参数可以使任意个
override, virtual方法可以再继承类中重写
overload, 重载方法
使用方法时也要书写ref与out
事件,是一个让类或者对象能发出通知的成员,event类型必须为委托类型
event关键字可以将操作限制为+=, -=
索引,可以被重载
>事件与委托例子
事件的行为和一个委托类型的变量没有区别(事件不是抽象的,也没有声明访问器),客户端通过事件句柄来响应事件,添加事件的时候必须是EventHandler类型,可以使用函数句柄创建事件处理器
type functionName(ojbect sender, EventArgs e){}
objClass.event += new EventHandler(functionName);
;
delegate void WorkComplete();
class worker
{
public WorkComplete completed; //delegate field, not event
public event WorkComplete realCompleted;
}
class boss
{
public void WorkCompleted()
{...}
}
class program
{
static void Main()
{
worker peter = new worker();
boss b = new boss();
peter.completed += boss.WorkComplete; //ok
peter.completed = boss.WorkComplete; //compile ok
peter.realCompleted += boss.WorkComplete; //ok
peter.realCoompleted = boss.WorkComplete; //compile error
}
}
>C#中,不到必要时候不要实现析构函数
>struct, 结构不需要再堆上分配,结构的赋值是值拷贝,也是使用new新建
new structType[total];
>数组
new int[100];
new int[5,2,4]
;
锯齿数组
int[,] a = new int[2][];
a[0] = new int[5];
a[1] = new int[10];
>接口
CWALINA说,也许缺少多重继承而使用接口代替是.net框架复杂的一个重要原因
;
一般的接口继承
;
实现接口的类可以看做对应接口的类型进行操作
;
可以用显示的接口成员实现,这样实现的接口就可以不是public类型的
class className:IControl
{
void IControl.Paint(){}
}
objClassName.Paint(); //error
IControl icontrol = new className;
icontrol.Paint(); //OK
>枚举,enum,C#中枚举可以有基础类型
enum name:sbyte
{
value = -1,
center = 0,
right = 1
}
>委托,具有类型的函数指针,定以后在创建对象
delegate returnType delegateName(parameters);
delegateName vardelegate = someFitFunctionName;
>特性,是修饰符,用于类型,成员,等,用于控制行为。用户可以自定义自己的特性
public class HelpAttribute:Attribute
{
string url;
public HelpAttribute(sring url)
{
this.url = url;
}
public string Url
{ get; set;}
}
从system.Attribute继承
使用时在相关的声明之前在括号里给出它的名字以及所有参数,如果一个特性的名字以Attribute结尾那么在使用的时候可以省略Attribute部分
[Help("someUrl")]
public class Widget
{
[Help("comeUrl")]
public void Display(string text)
{}
}
特性构造函数的参数才引用时给出,这样特性就被附加到指定的类上。额外的属性信息等可以通过引用特性公共的读写属性提供。例如运行时通过反射获取
;
运行时反射获取特性信息
using system;
using system.reflection;
class Test
{
static void showHelp(memeberInfo memeber)
{
HelpAttribute a = Attribute.GetCustomAttribute(member, typeof(HelpAttribute)) as HelpAttribute);
if( aa == null)
{
console.writeLine("No Help for {0}", member);
}
else
{
consol.writeLine("Help for {0}", member);
console.writeLine("Url={0}", a.Url);
}
}
static void Main()
{
showHelp(typeof(widget));
}
}
:词法结构
>编译程序步骤
1,转换到unicode
2,把unicode流翻译为标记流
3,词法分析,翻译为可执行单元
>unicode字符转义序列
\uhex-digit, \Uhex-digit
>整型字面量
后缀
u/U, uint
l/L, long
ul/UL/ ulong
f, float
d, double
m, decimal
>预处理
#define, #undef, #if, #elif, #else, #endif, #line, #error, #warning, #region, #endregion, #pragma编译器指令,例如关闭警告等
;
条件编译
#define Enterprise
#if Enterprise
#define Advanced
#end if
;
#if Debug
...
#else
...
#end if
;
#pragma warning disable 612
:基本概念
>应用程序入口点
static void main(){}
static void main(string[] args){}
static int main(){}
static int main(string[] args){}
>作用域与命名空间
作用域是指变量无须特殊生命即可访问的范围
命名空间是需要声明的
>就签名而言
object, dynamic认为是相同的
>最好不要手动调用垃圾回收期强迫回收资源
:类型
指向数据的引用,这个数据通常也称为对象
C#中任何类型的值都可以看做一个对象,引用类型直接可以看作为object类型就可以作为对象处理了,值类型需要进行装箱和拆箱操作才能看作为对象
装箱拆箱的目的,可以看作为引用类型来操作
>值类型,继承自system.valueType
>dynamic与object
区别
可以进行动态绑定
如果dynamic与object都是候选的,那么类倾向于选择dynamic
一个包含dynamic类型的表达式被称为动态表达式
不能作为泛型的基类型
:变量
>表达式
expr-first ?? expr-second
v 在expr-first之前的明确赋值状态和在expr之前相同
v 在expr-second之前的明确赋值状态和expr-first之后相同
>匿名函数
(parameter)=> {expresion;}
:转换
>隐式转换
>显示转换
type.parse(value);
(type)
>匿名函数到委托类型的转换会产生一个委托实例
:表达式
分为以下几种
值, 变量, 命名空间, 类型, 方法组, null, 属性访问, 索引访问, 什么都不是void
含有dynamic 类型的表达式称为动态表达式
>操作符重载
- 一元操作
+ - ! ~ ++ -- true false
- 二元操作
+ - * / % & | ^ << >> == != > < >= <=
>统一程序里,指定了两个同名属性序列和顺序的编译类型的匿名对象初始化会产生相同的匿名对象
>转换操作符
case ... (type) exception
>操作符
is
as, 转换不会抛出异常
>LINQ中的查询表达式
与SQL相反的语序
- 透明表达式, trasparent identifier, *, 透明标示符, 是查询翻译过程中的一个中间步骤存在的
当透明表达式在匿名函数里作为参数出现的时候, 相关匿名类型的成员会自动出现在匿名函数主体作用域
当含有透明标示符的成员在作用域里时, 该成员的成员也同样在作用域里
当透明标示符在匿名对象初始化语句里作为成员声明语句出现时, 它会引入一个含有透明标示符的成员
就是SQL语句中的"*"
:语句
>标签语句, 与goto配合
>lock语句, 会给给定的对象加上互斥锁, 执行语句后再释放
lock(obj)
{
...
}
>check, uncheck语句
>using, 获取一个资源后在块结束自动释放资源
using(obj/expression)
{
...
}
>yield语句
yield return expression;;
yield break;
yield不是保留字, 只有在return, break关键字之前才有特殊含义, 用来产生一个迭代器计数对象
:命名空间
>编译单元, 定义了一个源文件的总体结构, 编译单元由零或多个using指令加上零或多个全局特性, 再加上零或多个命名空间声明组成
每个编译单元的命名空间成员声明都向同一个被声明为全局命名空间的声明空间里添加成员
>命名空间声明
namespace n0;
namespace n0.n1;
namespace n0
{
namespace n1
{
...
}
}
>别名
extern alias identifier
>using, 可以简化定义在其他命名空间
using identifier = namespace-or-type-name;
可以让别名在特定的编译单元或命名空间主体里被访问到, 但是它不会再基础的命名空间里添加任何新成员
using namespace-name; 引用命名空间
namespace::property-name
:类
>修饰符
new, public, protected, internal, private, abstract, sealed, static
>抽象类
abstract class className
{
public abstract returnType functionName();
}
>封闭类
seal class className{}, 不能被继承
>静态类
static class className{}, 不能被实例化
>partial, 指明一个类是一个局部类声明
>类型参数
>静态类
static class, 不能被实例化, 不能作为类型使用,只能包含静态成员. 只有静态类才可以包含展开函数的声明.
- 不能包含sealed, abstract, 不能实例化与继承
- 不能包含类基础规范, 不能显示地指定基类或说实现接口的列表, 隐式继承于object
- 只包含静态成员, 常量与嵌套类型都为静态成员
- 不能含有protected, protected internal
>partial, 指明类声明式一个局部的类型声明, 类的每个局部定义都要有partial关键字
>特性
如果一个特性被房子在多个部分, 那么就相当于这个类型上多次指定了这个特性, 局部类型的特性是通过每个部分的特性无序组合在一起决定
>所有定义同样适用于泛型
>类允许嵌套定义
>隐藏父类方法或定义, 适用显示new关键字
new public type baseVarName{}
>嵌套型的this, 是指当前作用域内, 如果需要外层作用域的this, 那么需要在构造函数中传递进来
>嵌套层可以访问外层所有元素
>C#对事件, 属性, 方法都有保留的签名, 便于运行检查
>常量, constanct, 是一个表示在编译器就能计算出来的值
C#不允许出现datetime常量
>字段, 表示与一个对象或类相关联的字段成员, 可以包含一组特性, 一个new修饰, 一个4个访问修饰符组成的合法组合, readonly, volatile
>如果一个值不能再编译器就确定,但又希望作为常量使用, 那么使用static readonly 修饰是最好的选择
>ref 引用参数, out 输出参数都不会创建新的存储位置
>虚方法, virtual, 重写方法, override
>如果一个实例方法包含seal修饰, 那么必须包含override字符
>外部方法, extern, 通常也不是C#实现的方法. 外部方法的方法主体就是一个分号, 外部方法不可以是泛型
通常和DllImport特性一起使用, 这样可以让外部方法由动态链接库实现, 当外部方法由dllimport引入, 那么必须包含static修饰
[DllImport("kernel32", SetLastError=true)]
static extern bol CreateDirectory(string name, SecurityAttribute sa);
>局部方法, 当方法声明包含partial修饰符时. 只能在局部类或局部结构里. 不能定义访问修饰符, 都是私有的
, 返回值必须为void, 不可以现实的实现接口方法. 可以再不同的地方声明和实现, 但是都只能有一处
>扩展方法, 只能在非泛型, 非嵌套的静态类里声明. 第一个参数除了this之外, 不能有其他修饰符, 形参不能为指针
public static class Extendsings
{
public static int ToInt32(this string s)
{...}
public static T[] Slice<T>(this T[] source, int index, int count)
{...}
}
;
调用扩展方法
string[] strings = {"1", "22", "333"};
foreach(string s in strings.Slice(1,2)){...}
由于他们被声明为扩展方法, 所以可以直接用于strings[]上
等同于
foreach(string s in Extensions.Slice(strings, 1, 2)){...}
;
使用范围, 当一个设计师忘记为某个类添加某个方法的时候, 可以为其做补救
>属性, 一个可以访问对象或类的特征成员
包含static为静态属性, 否则就是实例属性
自动实现属性方法
public int propertyName{get; set;}
- 虚拟, 密封, 重写, 抽象访问器
>事件, 可以让对象或者类发出通知的成员, 客户端可以通过为事件提供句柄(event handler)来给事件附加可执行代码
scope event type name
经常将type定义为一个委托形式, 这样能保证事件处理函数具有统一的入口
public delegate void EventHandler(object sender, EventArgs e);
...
public event EventHandler Click;
- 可以自定义add, remove方法, 事件访问器
public event EventHandler Click
{
add;
remove;
}
类似于自动属性的实现,
;
external event, 外部事件, 由非程序代码实现的事件
- 静态事件, static, 不能与任何实例相关, 不能使用this关键字
- 虚拟, 密封, 重写, 抽象访问器
>索引, 是一个能让成员像数组那样呗访问的成员
索引不能使用static修饰
如果索引是一个借口的实例实现, 那么要使用.this关键字, 否则只是使用this关键字
public type this[formalParameterList]
{...}
>操作符
- 一元操作符
- 二元操作符
- 转换操作符
public static implicit operator SomeType<string> (ParameterList){...}
- implicit/explicit operator, 操作符声明中使用, implicit(含蓄的), explicit(明确的), 指定操作符必须是显示调用还是隐式自动调用
>构造函数
实现了要初始化一个类的实例必须进行的操作
- base, 引用基类
- this, 引用当前实例
- 可以使用类似C++的方式调用基类构造函数, 也可以在内部直接调用
public initFunction(parameterList):base(parameterList){...}
- 默认构造函数
- 私有构造函数, 如果一个类包含了静态成员且不打算实例化, 那么只要添加一个口空的私有构造函数即可, 私有构造函数的目的就是为了实现singleton模式, 来保证只有工厂方法才能创建那个对象的实例
- 可选的实例构造函数
public initFunction(parameter0):this(parameter0, parameter1){...}
public initFunction(parameter0, parameter1){...}
>静态构造函数, 最多只会执行一次.
在创建了一个类型的实例或者任何静态成员被第一次引用时触发.
普通类中可以定义静态构造函数.
静态构造函数不能被直接引用
class Test
{
static Test()
{...}
}
>析构函数
~ClassName(){...}, 是通过重写system.ojbect上的虚拟方法finalize来实现的
>迭代器, iterator, 用迭代器块实现的函数成员
>枚举接口, enumerator interface, 非泛型的system.collections.IEnumerator接口以及泛型接口System.Collections.Generic.IEnumerator<T>的所有实例
>yield, 对IEnumerator或IEnumerator<T>实现, 用于返回枚举对象
>计数器, 返回计数接口类型的函数成员
>MoveNext方法
>Current属性, 收到迭代器里德yield return语句影响
>dispose方法, 可以讲计数对象带入after状态来清理迭代
>GetEnumerator, 继承枚举器必须实现的接口
:结构
是值类型, 不要求在堆上分配, 构造类型的变量直接包含了结构数据
类类型的变量包含的是指向数据的引用
>partial修饰符,表示结构是局部类型
>结构接口, 可以说结构直接实现了给定的接口类型
>结构与类的区别
- 结构是值类型
- 所有构造类型都是隐式的继承自system.valueType类
- 对一个构造类型的赋值会创建一个被赋值的赋值
- 结构的默认值将所有的值类型字段设为默认值
- 装箱和拆箱操作可以用来将结构在object类型之间转换
- 对于结构来说this的含义不同, 在结构中this是变量, 在类中this是值, 可以用this来修改结构
- 结构的实例字段不能包含变量初始化
- 结构不能声明无参数的实例构造函数
- 结构不能声明析构函数
- 结构类型永远不会是抽象的, 一定是隐式封闭的
- 结构只能实现接口, 不能指定基类
>可以定义静态构造函数
>例如可以用结构实现语义内的自定义类型, 例如定义数据库的DBInt, DBBool
:数组
是一个包含多个变量的数据结构, System.Array类型是所有数组类型的抽象基础类型
>数组与泛型IList接口之间语句隐式转换
IList<string> listName;
>访问, 通过下标或者foreach语句枚举
>数组协变
如果A到B存在隐式引用转换, 或者显示引用转换, 那么数组类型A[R]到数组类型B[R]也存在相同的引用转换,R是秩序说明符. 这个关系就成为数组的协变. 意思是, 一个数组类型为A[R]的值实际上可能是一个指向数组类型B[R]的实例引用, 这里假设从B到A存在隐式引用转换
:接口
是一份契约, 实现类某个接口的类或者结构必须遵循这份契约. 接口可以继承多个基础接口, 而类或结构者可以实现多个接口
>可以是partial的
>如果型变注释是out, 者参数是协变的(covariant), 如果注释是in, 者参数类型是逆变(contravariant)的, 如果没有注释, 则类型参数是不变的(invariant)
interface C<out X, in Y, Z>
{
X m(Y y);
Z P{get; set;}
}
- 变量安全性
如果下面继承成立, 者类型T是输出不安全的
T是一个逆变类型参数
T是一个具有输出不安全的元素类型的数组类型
T是由一个种泛型类型S<X1, ..., Xk>构造的一个接口或者委托类型S<A1, ..., Ak>, 其中至少有一个Ai使得下列某项成立
Xi是协变或不变, 且Ai是输出不安全的
Xi是逆变或者不变, Ai是输入安全的
>基础接口
一个接口可以继承零到多个接口
>接口就是一个C++中的抽象类, 但是只是单纯的方法定义
>接口可以定义方法, 属性, 事件, 索引
>接口可以重新实现
>抽象类实现接口的时候可以再方法中调用抽象方法
:泛型
<>符号, 就是定义类型参数
:枚举, enum
是一个特殊的值类型
enum enumName:baseType
{..}
;
enum Color:long
{...}
- 都继承自System.Enum类型
- 枚举和证书之间的转换必须使用显示类型转换
:委托
可以处理其他语言需要使用函数指针的场景, 都继承自System.Delegate类
>委托的兼容性
D与M具有相同的形参, 每个形参拥有一样的ref, out
对于每个值参数, 从D的形参类型到M里相应的形参类型存在标示符转换或者隐式转换
对于每个ref或out参数, D里的形参类型和M里的形参类型相同
从M的返回类型到D的返回类型存在标示转换或隐式引用转换
>委托的实例有委托表达式创建
>委托类型是名字等价,而非结构等价, 只有名字相同才会认为相同
>使用委托
delegate type delegateName(parameters);
delegateName varDelegate = new delegateName(someFitFunctionName);
delegateName varDelegate1 = new delegateName(someFitFunctionName1);
delegateName varDelegate2 = varDelegate + varDelegate1
;
调用, varDelegate1(parameters)
>委托使用的时候要检测是否为null
:异常
所有的异常必须继承自System.Exception类型
- 所有的异常包含
Message, 是一个string类相关的只读属性, 描述信息
InnerException, 是一个Exception的只读属性, 如果它的值非null, 则指向引发当前异常的异常
:特性
例如一些框架中会定义一些HelpAttribute特性, 它可以放置在某些程序元素(如类和方法)上来提供程序元素到响应的文档映射
>特性是通过特性类定义声明的, System.Attribute
>使用方法
[AttributeUsage(attributeTargets.class|attributeTargets.Interface)]
public class SimpleAttribute:Attribute
{...}
>特性具有的属性
- AllowMultiple, 指定特性是否在一个类中可以被使用多次
- Inherited, 指定在使用的类的子类是否可以继承特性
>软件传奇人物
Brian Kernighan, Dennis Richie
>特性名在定义的时候默认包含Attribute, 使用时可以省略
>特性里位置形参和命名形参限制于以下类型
bool, byte, char, double, float, int, long, sbye, short, string, uint, ulong, ushort, object, system.type, enum, 嵌套类型
注意, decimal不能作为类型参数, 至多只能使用decimal常量
>特性规范
- 特性的作用范围
- 全局作用域上指定要作用于目标assembly, module
- 委托声明上指定的特性要么作用域这个被声明的委托, 要么作用域它的返回值
- 方法声明指定的特性,要么作用域被声明的方法, 要么作用于它的返回值
- 操作符声明上指定的特性要么作用域被声明的操作符, 要么作用于它的返回值
- 在省略了实际那访问器的事件声明上指定的特性可以作用于被声明的事件,相关联的字段,或者相关联的add,remove方法
- 在属性或索引声明的get访问器声明上指定的特性可以作用于相关的方法或它的返回值
- set访问器, 作用于方法或惟一的隐式形参
- 事件add, remove特性作用于相关的方法或者惟一的形参
>特性全局目标
assembly, module
>特性一般目标
field, event, method, param, property, return, type
>使用特殊字符, @来指定特性名称表明是全部特征名, 无须系统添加attribute
>定义特性
using System;
[AttriuteUsage(AttributeTargets.Class)]
public class HelpStringAttribute:Attriute
{
strng value;
public HelpStrngAttribute(strig value)
{
this.value = value;
}
public sting Value
{
get;
}
}
[HelpString("Description of Class")]
public class ClassName{...}
>保留特性
-System.AttributeUsageAttribute, 描述特性类可以使用的方式
System.Diagnostics.ConditionalAttribute, 用来定义条件方法
System.ObsoleteAttribute, 将一个成员标记为过时
;
-Conditional特性可以定义条件方法(conditional method)和条件特性(conditional attribute class)
[AttribteUsage(AttributeTarges.Method|AttributeTargets.Class, AllowMultiple = true)]
public class ConditionalAttribute:Attribute
{
public ConditionalAttribute(string conditionString)
{...}
public string ConditionStrin{get;}
}
-Conditional方法, 条件方法
条件方法必须在类声明或者结构声明中
条件方法返回类型必须为void
条件方法不能带有override, 可以含有virtual修饰符
条件方法不可以实现接口方法
;
例子
#define DEBUG
using System;
using System.Diagnostics;
class ClassName
{
[Conditional("DEBUG")]
public static void M()
{
Console.WriteLine("Executed ClassName method");
}
}
这时如果其他类引用ClassName的M方法, 只有在定义了DEBUG的情况下才会执行M方法
-Obsolete特性, 用来标记不应该再被使用的某些类型和类型的成员
被Obsolete特性修饰的成员被引用的时候编译时就会发出警告, 如果指定了错误参数并且设置为true就会给出错误提示
public class ObsoleteAttribute:Attribute{...}
;
使用
[Obsolete("This class is obsolete; use class B instead")]
classs A
{...}
编译器没有废弃对虚方法的重写, 当一个虚方法标记为obsolete时, 必须为重写方法手动添加obsolete
>用于互操作的特性, 只适用于.net实现的C#
-COM以及win32组件
例如DllImport, 可以用在一个静态外部方法来指明这个方法的实现可以再win32 dll里找到, 这些特性可以在System.runtime.InteropServices命名空间找到
-操作其他语言
IndexerName特性, 是由锁芯属性实现的, 在.net元数据里含有名字, 如果一个索引没有包含indexerName, 那么默认的名字就是item
namespace System.Runtime.CompilerServices.CSharp
{
[AttributeUsage(AttributeTargets.Property)]
public class IndexerNameAttribute:Attribute
{
public IndexerNameAttrbute(stringindexerName){...}
}
}
:不安全代码
为了编写实时性要求很高的算法, 提供的不安全代码段, 提供了类似于C++的性能, 必须使用unsafe修饰符标注
>可以使用不安全的类, 字段, 方法, 事件, 索引, 操作符, 实例, 块
unsafe
{...}
>非及特殊情况, 不需要使用不安全代码
>指针地址比较, 当做无符号整数比较
>fixed语句, 保证在指定的块内变量的地址不变
fixed(varName){...}
>栈分配
stackalloc type[size];
>直接访问堆空间
using System;
HeapAlloc(...)
HeapFree(...)