类和接口默认是public的,成员默认是private的。
C#中静态类不能实现接口,它里面所有的成员(包括成员类型,方法等)必须是静态的。
CLR不支持partial,那只是C#的语法,所以partial class必须用C#写。
virtual和override,前者用于父类标识可重构的成员,后者用于子类的重构,virtual方法最好少用,因为性能会降低。
sealed标识密封类,即不可继承类,不能使用virtual关键字,类申明成sealed可以提高性能,因为可以减少很多检查和验证。
new除了实例化对象外,当子类的方法签名和父类一样时,用new可以更清楚地表达子类的方法和父类没有任何关系,如果不用的话,编译时会警告,但还是会隐藏父类方法,使用子类自己定义的方法。
常量的值必须在编译时就确定,编译后,CLR会把常量的值保存在程序集的元数据中,如果代码中使用到常量,CLR会去元数据中查找并把常量值放入IL中。
静态字段在类型第一次被引用时初始化。实例字段则是当实例被创建时才初始化。
.ctor是实例成员初始化(包括构造方法),.cctor是静态类型初始化,.cctor在.ctor之前执行。.ctor执行顺序是,如果实例成员在声明时也被赋值则先执行,然后是基类构造方法,最后是该类的构造方法。
声明成员的同时进行赋值也叫内联初始化。
readonly用于类型时,表示该类型引用不能更改,但是引用指向的对象可以更改。
params用于定义数量不确定的参数,结构为“params 类型名[] 变量名”,它必须位于所有参数的最后一个,不能与out/ref共用,对于不同类型参数的传入,可以用“params object[] 变量名”。
方法的参数尽量用接口,返回值尽量用具体类。
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(){...}
值类型可以转换成接口,不过需要先装箱。
委托定义于类之外,类中定义委托成员。委托是方法的引用,引用方法时,允许委托参数是方法参数的子类,方法的返回值是委托返回值的子类。当一个委托被引用了多个方法时,如果有多个方法有返回值,则委托返回最后一个方法的返回值。当委托引用多个方法并被调用时,如果某个方法抛出异常,则后续方法将不会被执行,这个问题可以通过先调用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。泛型如果被指定为引用类型则可以用==和!=来比较,被指定为值类型则不能这么比较,除非你重载了这些操作符,啥都不指定自然也不能比较。
自定义属性Attribute,有三个设置,第一个是AttributeTargets用来设置属性的目标范围,比如Class,Method,Enum,All等等,可以用操作符"|"来扩充适用范围,默认值是All;第二个是Inherited,true表示该属性自动用于类的子类,false则相反,默认值是true;第三个AllowMultiple表示该属性是否可以多次用于同一个目标,默认值是false。