1.1类型的各种成员
1.常量
2.字段
字段如果是静态的,这种情况会被认为是类型状态的一部分,在类型构造器(静态构造函数)初始化。
字段也可以是实例(非静态),这种情况会被认为是对象状态的一部分,在实例构造器中初始化。
3.实例构造器
新对象的实例字段初始化
4.类型构造器
类型的静态字段初始化
5.方法
作用:更改或查询一个类型或对象的状态。
当作用于类型时,成为静态方法
当作用于对象时,称为实例方法
6.操作符重载
其实是一个方法,它定义了将一个特定的操作符作用于对象时,应该如何操作这个对象,但不是CLS的一部分。
7.操作符转换
定义了如何隐式或显示地将对象从一种类型转型为另一种类型的方法,也不是CLS的一部分
8.属性
可以使用一种简单的、字段风格的语法来设置或查询类型或对象的部分逻辑状态,同时保证状态不被遭到破坏
作用于类型的称为静态属性
作用于对象的称为实例属性
9.事件
利用静态事件,一个类型可以向一个或多个静态或实例方法发送通知。
利用实例事件,一个对象可以向一个或多个静态或实例方法发送通知。
提供事件的类型或对象的状态发生改变,通常会引发事件,事件包含两个方法,允许静态或实例方法登记或注销对该事件的关注。除了这两个方法,事件通常还使用一个委托字段来维护已登记的方法集。
10.类型
类型可定义嵌套于其中的其他类型。
public sealed class SomeType { //嵌套类 private class SomeNestedType { } //常量、只读字段和静态可读/可写字段 private const Int32 SomeConstant = 1; private readonly Int32 SomeReadOnlyField = 2; private static Int32 SomeReadWriteField = 3; //类型构造器 static SomeType() { } //实例构造器 public SomeType() { } public SomeType(Int32 x) { } //实例方法和静态方法 public String InstanceMethod() { return null; } public static void StaticMethod { } //实例属性 public Int32 SomProp { get { return 0; } set { } } //实例有参属性(索引器) public Int32 this[String s] { get { return 0; } set { } } }
无论使用的编程语言是什么,编译器都必须能处理你的源代码,为上述每一种成员生成元数据和IL代码,而且格式都是完全一致的,所以元数据是所有语言生成和使用的公共信息,正因为有了元数据,用一种语言写的代码才能无缝访问另一种语言写的代码,元数据是整个Microsoft .net framework开发平台的关键,它实现了编程语言、类型和对象的无缝集成。
1.2类型的可见性
//以下类型的可见性为public,既可以由本程序集的代码访问, //也可以由其他程序集中的代码访问 public class ThisIsAPublicType { } //以下类型的可见性为internal,只可由本程序集中的代码访问 internal class ThisIsAnInternalType { } //由于没有显示声明类型的可见性,所有默认为internal class ThisIsAlsoInternalType { }
友元程序集
假定情形:某公司团队TeamA、TeamB,每个团队都各自生成自己的程序集,TeamB的程序集要使用TeamA的类型,TeamA要把他们的类型设为public,意味着要对所有程序集公开。就连另一家公司的开发人员都能写代码来用这些类型,这就是问题所在。
现在我们希望TeamA能用个办法来将他们的类型定义为internal,同时允许团队TeamB使用。所以C#和CLR通过友元程序集(friendly Assembly)来提供这方面的支持。
相关的命名空间:System.Runtime.ComplierServices
相关特性:InternalsVisibleTo的Attribute来标明它认为是友元的其他程序集
using System.Runtime.CompilerServices; //当前程序集中internal类型可以由以下两个程序集中 //的任何代码访问(不管什么版本或语言文化) [assembly:InternalsVisibleTo("Lord,PublicKey=12345678...90abcdef")] [assembly: InternalsVisibleTo("Microsoft,PublicKey=b77a5c56...1934e089")] internal sealed class SomeInternalType { } internal sealed class AnotherInternalType { }
1.3成员的可访问性
表格:
CLR术语
|
C#术语
|
描述
|
Private | private | 成员只能定义类型或任何嵌套类型中的方法访问 |
Family | protected | 成员只能定义类型、任何嵌套类型或者不管在什么程序集中的一个派生类型中的方法访问 |
Family and Assembly | 不支持 | 成员只能右定义类型、任何嵌套类型或者同一程序集中定义的任何派生类型中的方法访问 |
Assembly | internal | 成员只能由定义程序集中的方法访问 |
Family or Assembly | protected internal | 成员可由任何嵌套类型、任何派生类型(不管什么程序集)或定义程序集中的任何方法访问 |
Public | public | 成员可由任何程序集的任何方法访问 |
注意:
1.C#禁止显示指定接口的成员的可访问性(编译器会自动将成员设为public)
2.派生类重写基类,对C#来说基类成员是protected的,重写成员也必须是protected,但对于CLR来说允许放宽成员的可访问性限制,比如基类成员protected,重写成员可以是public,不能是private
1.4静态类
1.永远不需要实例化的类,里面必须只有静态成员,不允许有实例成员(包括实例构造器)
2.在C#中使用static关键字定义不可实例化的类,这个关键字只能用于类,不能用于结构(值类型),因为CLR总是允许值类型实例化,这是没办法阻止的。
C#编译器对静态类进行了如下限制:
1.静态类必须直接从基类System.Object派生,从其他任何基类派生都没有任何意义,继承只适用于对象,而你不能创建静态类的实例。
2.静态类不能实现任何接口,这事因为只有使用类的一个实例时,才可以调用类的接口方法。
3.静态类只能调用静态成员(字段、方法、属性和事件)。
1.5分布类、结构和接口
分布类、结构和接口功能完全是有C#编译器提供的,对于CLR是一无所知。
partial关键字告诉编译器,一个类、结构或者接口定义的源代码可能要分散到一个或多个源代码文件中,这主要有三方面原因促使我们这么做:
1.源代码控制
比如一个类型的定义有很多很多的源代码构成,如果一个程序员在系统中签出进行修改,那么其他程序员就无法同时修改这个类型了,除非执行一次合并。
使用partial关键字,可以将类型的代码分散到多个源代码文件中,每个文件都可以单独签出修改。
2.在同一个文件中,将一个类或结构分解成不同的逻辑单元
当我们创建一个类型来提供多个功能,使得类型能提供一个完整的解决方案,有时我们会在一个源代码问价中反复声明同一个分部类型,分部类型的每一个部分都实现一个功能,并配套它的全套字段、方法、属性、事件等。方便的删除一个完整的功能,即可把分部类型的一部分注掉。
3.代码拆分
最好的例子是WebForm或Windows窗体,新建时会自动生成cs文件和designer.cs问价
总而言之,言而总之,编译时,C#编译器会对所有分部类型代码合并,最后的exe或dll程序集文件中生成一个类型,但注意所有分部类型必须是同一种编程语言。
1.6组件、多态和版本控制
C#关键字及其对组建版本控制的影响
表格:
C#关键字
|
类型
|
方法/属性/事件
|
常量/字段
|
abstract | 表示不能构造该类的实例 | 表示为了构造派生类型的实例,派生类型必须重写并实现这个成员 | 不允许 |
virtual | 不允许 | 表示这个成员可有派生类重写 | 不允许 |
override | 不允许 | 表示派生类型重写了基类型的成员 | 不允许 |
sealed | 表示该类型不能用作基类型 | 表示这个成员不能被派生类型重写,只能将该关键字应用于准备重写一个虚方法的方法 | 不允许 |
new | 应用于嵌套类型、方法、属性、事件、常量或字段时,表示该成员与基类中相似的成员无任何关系 |
合理的使用类型的可见性和成员的可访问性
密封类之所以比非密封类更好,有以下三个方面的原因:
1.版本控制
如果最开始是密封类,将来可以在需要时改为非密封类,但是,如果是非密封类,将来就不可能把它更改为密封的,因为这将终端派生。
另外,如果非密封类定义了任何非密封的虚方法,就必须在新版本的类中保持虚方法的调用顺序,否则可能中断派生类。
2.性能
3.安全性和可预测性
类必须保护它自己的状态,不允许自己被破坏。如果处于非密封状态时,只要它的成员不是私有的,派生类型就能访问和更改基类的状态,一旦将某个方法、属性、事件设为virtual,基类就会丧失对它的行为和状态的部分控制权。