第4讲:杂项技术,以及C#语言的未来发展
2005.9.12 李建忠
Agenda
属性访问器的保护级别
命名空间别名限定符
Pragma指示符
Conditional特性类
定长buffer
C# 3.0与未来发展
属性访问器保护级别的变化
C# 2.0允许我们对一个属性的get和set访问器使用不同的访问级别修饰符:
这里get是public修饰,set是internal修饰。Name前面是属性的访问修饰符,而get和set前面是属性访问器的修饰符。这种不同就会带来不一致性。因此对于修饰符有一些注意点。
一些注意点
属性访问器(get或set)上的应用的访问修饰符必须“小于”属性上应用的访问修饰符;“小于”的意思即“更严格”,例如protected小于public。
例如我们上面的例子Name的修饰不能是protected,因为internal和protected是平级的,不存在小于关系。
只能在一个属性访问器(get或set)上指定比属性上的访问修饰符“更小”的访问修饰符。
例如上面的例子,我们不可以再给get添加上internal修饰。因为既然get和set都是internal,属性Name的public修饰符就没有意义了,这是编译器的一个合理推断。
对于接口中属性的声明,不能给属性访问器(get或set)指定任何访问修饰符,只能默认为public。
属性访问器保护级别的变化规则完全适用于C#的索引器。
命名空间别名限定符的引入
C# 2.0允许我们使用命名空间别名限定符(::)来避免不同命名空间中类型名称冲突的问题:
中间我们最好不要用点号,虽然可以用,但是C#引入双冒号就是为了避免使用点号。
一些注意点
当使用命名空间别名限定符(::)时,如果ZC::ArrayList,编译器可以确保这是一个只适用于“命名空间别名”的限定符,不会辨析为其他类型、或者成员限定符(.)。
假如碰巧有一个ZC的类,如果用点号,编译器就会辨析ZC.ArrayList为ArrayList是不是ZC的成员,这样就会是一个莫宁两可的结果。因此我们用双冒号效率会有一点点提升,它不会去找类型了,直接就去找命名空间别名了。但这也仅仅是编译时的问题,这更重要的是避免了模糊不清的判断,方便辨析。
关键字global可以放在命名空间别名限定符(::)的左边,它使得编译器只去搜索那些所有的命名空间,而不会去搜索其他的类型、或者成员。
尽可能地使用命名空间别名限定符(::),而减少使用点号(.)这样的通用限定符。
Pragma指示符的引入
C# 2.0允许我们使用#pragma指示符来指定编译器对警告信息的处理:
标示了Obsolete标签的方法表示是废弃的方法,提示让大家不再使用这个方法。但是例如在写库程序的时候,考虑到兼容,我们希望把这些警告信息摒除掉。#pragma之间包含的是要被disable的内容,后面的数字可指定可不指定,不指定表示disable所有的警告信息。如果有数字表示只禁用这个号码的警告信息。
几个注意点
目前Pragma指示符只支持#pragma warning。
#pragma warning disable可以禁掉任何编译器警告信息。
#pragma warning restore可以恢复被disable掉的任何编译器警告信息。
可以在disable和restore后面跟上具体的警告代码号,从而来禁止或者恢复特定的警告信息。
#pragma是一个编译预处理功能,不影响任何代码运行机制。
Conditional特性类的引入
C# 2.0允许我们使用Conditional特性类来告诉编译器根据“特定的预定义指示符条件”来在类上应用特性:
C# 1.0的时候是只能定义在方法上,如果定义了这个符号,这个方法就会被调用,如果没有定义,这个方法就不会使用。C# 2.0扩展到在类上也可以使用这种符号定义。
例子中,首先我们在最开始写了一个预定义的指示符,然后写了一个特性类TestAttribute,在特性类上运用了一个Conditional特性,这个特性传了一个字符串参数,表示这个特性类将成为一个条件性的特性类。如果定义了传入参数的指示符,那么Test特性就会被应用在MyClass上面,也就是编译器在编译MyClass类的时候,会在MyClass类上产生一个特性元数据,特性元数据是在编译时实例化的。如果我们注释掉#define DEBUG,那么Test标签相当于也被注释了,代码将不会在MyClass上应用这个特性了。
几个注意点
如果定义了条件指示符,如#define DEBUG,那么编译器将在MyClass类上应用TestAttribute特性。
如果没有定义条件指示符,如#undef DEBUG,MyClass类照样可以使用,但是其上将不再应用TestAttribute特性。
注意区别C# 2.0中的Conditional特性类和C# 1.0中的Conditional特性方法。
定长buffer的引入
C# 2.0引入定长buffer来使得我们可以在结构里声明C风格的数组,从而更加方便地实现托管代码和非托管代码的互操作:
注意,需要在数组前加一个fixed关键字,这个fixed和C# 1.0的fixed语句不一样,1.0的fixed语句是把在托管堆中的东西固定住,以便用指针访问,防止垃圾收集器搬移这个内存,与非托管代码进行互操作。2.0中这个fixed就是指定一个定长的buffer,这里这个结构中,一个int4个byte,一共4*115=460个byte。
几个注意点
定长buffer只能使用在unsafe代码的上下文中,不可以在非unsafe的代码中使用。
使用定长buffer所定义的字段在结构类型的实例对象中将按照它们的声明顺序来进行内存布局。
注意区别unsafe代码中的定长buffer和我们通常使用的托管数组。
两者有很大区别。一般正常托管数组的声明是int[] myArray,中括号的位置,以及定长数组一定要明确数组有多长。定长的buffer是不继承自System.Array的,托管的数组是默认继承自System.Array的。
定长buffer主要应用在托管代码和非托管代码互操作的情况,除此之外,我们一般使用托管数组。
C# 3.0与未来发展
C# 2.0的核心机制在于泛型编程的引入,它赋予了类型参数式多态的能力,将对今后的C#代码构造有重要影响。
研发中的C# 3.0将XML,SQL两种数据处理技术引入到C#这样的强类型语言中,极大地丰富了C#语言的数据处理能力,是一个极具远见的创新。
C#语言的发展越来越多地体现融合“设计模式+库”的思想。“语言的发展就是库的发展!”
2010.11.2