对.Net Framework的认识(2)
类和接口的访问权限默认是internal,接口成员不能添加访问修饰符,默认是public,类成员默认是private的。
C#中静态类不能实现接口,它里面所有的成员(包括成员类型,方法等)必须是静态的。
CLR不支持partial,那只是C#的语法,所以partial class必须用C#写。
virtual和override,前者用于父类标识可重构的成员,后者用于子类的重构,virtual方法最好少用,因为性能会降低。因为非virtual方法,CLR可以很快定位到这个方法的出处,而virtual方法,CLR得先去找调用该方法的对象是哪个类型的,在确定类型之后才能定位方法的出处,如果派生层次很多很复杂的话,性能消耗就会比较大。
sealed标识密封类,即不可继承类,不能同时使用virtual关键字,类申明成sealed可以提高性能,因为可以减少很多检查和验证。
new除了实例化对象外,当子类的方法签名和父类一样时,用new可以更清楚地表达子类的方法和父类没有任何关系,如果不用的话,编译时会警告,但还是会隐藏父类方法,使用子类自己定义的方法。
常量的值必须在编译时就确定,编译后,CLR会把常量的值保存在程序集的元数据中,如果代码中使用到常量,CLR会去元数据中查找并把常量值放入IL中。常量如果为引用类型(String除外),则只能赋值null。类中的常量字段引用方式和静态字段一样,即类名.字段名。
静态字段在类型第一次被引用时初始化。实例字段则是当实例被创建时才初始化。
.ctor是实例成员初始化(包括构造方法),.cctor是静态类型初始化,.cctor在.ctor之前执行。.ctor执行顺序是,如果实例成员在声明时也被赋值则先执行,然后是基类构造方法,最后是该类的构造方法。
声明成员的同时进行赋值也叫内联初始化。
readonly用于类型时,表示该类型引用不能更改,但是引用指向的对象可以更改。
params用于定义数量不确定的参数,结构为“params 类型名[] 变量名”,它必须位于所有参数的最后一个,不能与out/ref共用,对于不同类型参数的传入,可以用“params object[] 变量名”,不过使用params关键字对性能损耗有影响,所以能不用还是不用比较好。
方法的参数尽量用接口,返回值尽量用具体类。
String是个常量,一旦赋值无法更改,要更改只有生成新的String。
StringBuilder其实就是个Char数组。
枚举类中多个符号对应一个值时,值转符号只会返回第一个符号。枚举成员类型默认是int型,但可以是int,uint,byte,sbyte,long,ulong,short,ushort这8种类型。枚举类型可以用运算符。GetUnderlyingType方法可以获取枚举类型对应的类型,如System.Int32,使用例子如:Enum.GetUnderlyingType(typeof(Color));
数组是引用类型,[]和System.Array是一回事。
接口中不可以有静态成员,隐式实现接口的方法必须声明为public。
显式实现接口的方法,即使用接口名.方法实现接口方法,不能添加访问权限标志,默认为private,只能通过接口变量访问,不是类对象的一部分(虽然是在类中实现的),比如void ITest.Test(){...}
值类型可以转换成接口,不过需要先装箱。比如:int i=1; IFormattable f=(IFormattable)i;这个过程中i就被装箱了。
委托定义于类之外,类中定义委托成员。委托是方法的引用,引用方法时,允许委托参数是方法参数的子类,方法的返回值是委托返回值的子类。当一个委托被引用了多个方法时,如果有多个方法有返回值,则委托返回最后一个方法的返回值。当委托引用多个方法并被调用时,如果某个方法抛出异常,则后续方法将不会被执行,这个问题可以通过先调用GetInvocationList来获取方法数组,然后遍历数组,加上try、catch后调用各个方法来解决。
泛型支持值类型和引用类型,但不支持枚举类型。泛型只能使用Object的方法,如果想增加可使用的方法,可以实现接口,用where语句,比如where T:IComparable<T>。泛型如果指明是值类型,则可以用new关键字创建对象,比如class A<T> where T:struct,或者是实现无参构造器约束,比如class A<T> where T:new(),也可以使用new创建对象。另外,class A<T> where T:class这个约束说明T可以是类/接口/委托/数组中任何一种。可以使用关键字default来为泛型设置默认值,比如:T temp=default(T)其中默认值为null或者0,两个泛型如果被约束为引用类型,则可以使用==或!=来比较,如果被约束为值类型,则不可以用这两个操作符比较,除非你自己重载,如果没有约束,自然也不能比较。
自定义属性有三个设置,第一个是AttributeTargets,表示属性可以用于的范围,比如Class,Method,All等等,默认是All,可以用操作符"|"来合并多个范围;第二个是Inherited,表示该属性是否可以自动用于类的子类,默认是true;第三个是AllowMultiple,表示该属性是否可以多次用于一个对象,默认是false。
checked和unchecked关键字用于是否做溢出检查,但对Decimal类型无效,默认是unchecked,因为checked会降低性能。