常量

一个值不可以改变静态字段
在编译时值就已经定下来了。
任何使用常量的地方,编译器都会把这个常量替换为它的值
常量的类型可以使内置的数值类型、bool、char、string、或enum
使用const关键字声明,声明时必须使用具体的值来对其初始化

  1. public const string thisconst = "Hello";

也可以在方法里定义本地常量

  1. static void Main()
  2. {
  3. const string thisconst = "Hello";
  4. }

常量与静态只读字段

常量比静态只读字段更严格:

  • 可使用的类型
  • 字段初始化的语义上(必须有值)

常量是在编译时进行值的估算的

注意

当值有可能改变,并且需要暴露给其它Assembly的时候,静态只读字段是相对较好选择

  1. public const decimal ProgramVersion = 2.3;

如果Y Assembly引用了X Assembly并且使用了这个常量,那么在编译的时候,2.3这个值就会被固化于Y Assembly里。这意味着,如果后来X重编译了,这个常量变成了2.4,如果Y不重新编译的话,Y将仍然使用2.3这个值,直到Y被重新编译,它的值才会变成2.4。静态只读字段就会避免这个问题的发生

静态构造函数

  • 静态构造函数,每个类型执行一次
  • 非静态构造函数,每个实例执行一次
  • 一个类型只能定义一个静态构造函数
    • 必须无参

方法名与类型一致

  1. class classname
  2. {
  3. static classname()
  4. {
  5. System.Console.WriteLine("Type Initialized");
  6. }
  7. }
  • 在类型使用之前的一瞬间,编译器会自动调用类型的静态构造函数:
    • 实例化一个类型
    • 访问类型的一个静态成员
  • 只允许使用unsafeextern修饰符
  • 如果静态构造函数掏出了未处理的异常,那么这个类型在该程序的剩余生命周期内将无法使用了
  • 初始化顺序
    • 静态字段的初始化器在静态构造函数被调用之前的一瞬间运行(也就是静态字段在静态构造函数之前运行的)
    • 如果类型没有静态构造函数,那么静态字段初始化器在类型被使用之前的一瞬间执行(最晚执行),或者更早,在运行的突发奇想的时候执行
    • 静态字段的初始化顺序与它们的声明顺序一致
      • 未命名图片.png

静态类

类也可以是静态的
其成员必须全是静态的
不可以有子类
例如

  1. System.Console
  2. System.Math


FINALIZER终结器(析构函数)

Finalizer是class专有的一种方法
在GC回收为引用对象的内存之前运行
其实就是对object的Finalize()方法重写的一种语法

  1. class classname
  2. {
  3. ~classname() { }
  4. }

编译生成的:

  1. protected override void Finalize()
  2. {
  3. ...
  4. base.Finalize();
  5. }

C#7可以这样写

  1. ~classname() => System.Console.WriteLine("Finalizing");

PARTIAL TYPE局部类型

允许一个类型的定义分布在多个地方(文件)
典型应用:一个类的一部分是自动生成的,另一部分需要手动写代码:

  1. //PaymentFromGen.cs
  2. - auto-generated
  3. partial class PaymentForm{..}
  4. //PaymentForm.cs -
  5. hand-authored
  6. partial class PaymentForm{..}

每个分布的类都必须使用partial来声明
下面这个例子就会报错:

  1. partial class PaymentForm{..}
  2. class PaymentForm{..}

每个分布类的成员不能冲突,不能有同样参数的构造函数
各分布类完全靠编译器来解析:每个分布类在编译时必须可用,且在同一个Assembly
如果有父类,可以在一个或多个分布类上指明,但必须一致
每个分布类可以独立的实现不同的接口
编译器无法保证各分布类的字段的初始化顺序

PARTIAL MRTHOD局部方法

  • partial类型可以有partial method
  • 自动生成的分布类里可以有partial method,通常作为“钩子”使用,在另一部分的partial method里,我们可以对这个方法进行自定义
  • partial method由两部分组成:定义个实现
  • 定义部分通常是生成的
  • 实现部分通常是手动编写的
  • 如果partial method只有定义,没有实现,那么编译的时候该方法定义就没有了,调用该方法的代码也没有了。这就允许自动生成的代码可以自由的提供钩子,不用担心代码膨胀
  • partial method必须是void,并且隐式private的

NAMEOF 操作符 C#6

nameof操作符会返回任何符号(类型、成员、变量。。。)
利于重构

  1. int Count = 123;
  2. string name = nameof(Count); // name is "Count"