常量
一个值不可以改变的静态字段
在编译时值就已经定下来了。
任何使用常量的地方,编译器都会把这个常量替换为它的值
常量的类型可以使内置的数值类型、bool、char、string、或enum
使用const关键字声明,声明时必须使用具体的值来对其初始化
public const string thisconst = "Hello";
也可以在方法里定义本地常量
static void Main()
{
const string thisconst = "Hello";
}
常量与静态只读字段
常量比静态只读字段更严格:
- 可使用的类型
- 字段初始化的语义上(必须有值)
注意
当值有可能改变,并且需要暴露给其它Assembly的时候,静态只读字段是相对较好选择
public const decimal ProgramVersion = 2.3;
如果Y Assembly引用了X Assembly并且使用了这个常量,那么在编译的时候,2.3这个值就会被固化于Y Assembly里。这意味着,如果后来X重编译了,这个常量变成了2.4,如果Y不重新编译的话,Y将仍然使用2.3这个值,直到Y被重新编译,它的值才会变成2.4。静态只读字段就会避免这个问题的发生
静态构造函数
- 静态构造函数,每个类型执行一次
- 非静态构造函数,每个实例执行一次
- 一个类型只能定义一个静态构造函数
- 必须无参
方法名与类型一致
class classname
{
static classname()
{
System.Console.WriteLine("Type Initialized");
}
}
- 在类型使用之前的一瞬间,编译器会自动调用类型的静态构造函数:
- 实例化一个类型
- 访问类型的一个静态成员
- 只允许使用unsafe和extern修饰符
- 如果静态构造函数掏出了未处理的异常,那么这个类型在该程序的剩余生命周期内将无法使用了
- 初始化顺序
- 静态字段的初始化器在静态构造函数被调用之前的一瞬间运行(也就是静态字段在静态构造函数之前运行的)
- 如果类型没有静态构造函数,那么静态字段初始化器在类型被使用之前的一瞬间执行(最晚执行),或者更早,在运行的突发奇想的时候执行
- 静态字段的初始化顺序与它们的声明顺序一致
静态类
类也可以是静态的
其成员必须全是静态的
不可以有子类
例如
System.Console
System.Math
FINALIZER终结器(析构函数)
Finalizer是class专有的一种方法
在GC回收为引用对象的内存之前运行
其实就是对object的Finalize()方法重写的一种语法
class classname
{
~classname() { }
}
编译生成的:
protected override void Finalize()
{
...
base.Finalize();
}
C#7可以这样写
~classname() => System.Console.WriteLine("Finalizing");
PARTIAL TYPE局部类型
允许一个类型的定义分布在多个地方(文件)
典型应用:一个类的一部分是自动生成的,另一部分需要手动写代码:
//PaymentFromGen.cs
- auto-generated
partial class PaymentForm{..}
//PaymentForm.cs -
hand-authored
partial class PaymentForm{..}
每个分布的类都必须使用partial来声明
下面这个例子就会报错:
partial class PaymentForm{..}
class PaymentForm{..}
每个分布类的成员不能冲突,不能有同样参数的构造函数
各分布类完全靠编译器来解析:每个分布类在编译时必须可用,且在同一个Assembly里
如果有父类,可以在一个或多个分布类上指明,但必须一致
每个分布类可以独立的实现不同的接口
编译器无法保证各分布类的字段的初始化顺序
PARTIAL MRTHOD局部方法
- partial类型可以有partial method
- 自动生成的分布类里可以有partial method,通常作为“钩子”使用,在另一部分的partial method里,我们可以对这个方法进行自定义
- partial method由两部分组成:定义个实现
- 定义部分通常是生成的
- 实现部分通常是手动编写的
- 如果partial method只有定义,没有实现,那么编译的时候该方法定义就没有了,调用该方法的代码也没有了。这就允许自动生成的代码可以自由的提供钩子,不用担心代码膨胀
- partial method必须是void,并且隐式private的
NAMEOF 操作符 C#6
nameof操作符会返回任何符号(类型、成员、变量。。。)
利于重构
int Count = 123;
string name = nameof(Count); // name is "Count"