参考资料:
《C# 7.0 本质论》
B站杨旭C#教程:https://www.bilibili.com/video/BV1k4411H7aM?p=4
下面的一部分内容是来自《C# 7.0 本质论》,还有一部分是我直接把杨旭C#教程的PPT手打了一遍,为了促进我的思考。建议朋友们直接去看他的教程,还辅以他的一些讲解,更加易懂。
- 什么是static
- 什么是静态字段
- 什么是const(常量)
- 什么是readonly
- 常量(const)与静态只读字段的区别
- 什么是静态构造函数
- 静态字段和静态构造函数的初始化顺序
- 什么是静态属性
- 什么是静态类
- 什么是Finalizer(终结器或析构函数)
- 感受
什么是static
C#没有全局变量或全局函数,在C#中与全局字段或函数等价的是静态字段或方法。“全局变量/函数”和“C#静态字段/方法”在功能上没有差异,只是静态字段/方法可包含访问修饰符,比如private,从而限制访问并提供更好的封装。
什么是静态字段
- 使用static关键字定义能由同一个类的多个实例共享的数据
- 和实例字段(非静态字段)一样,静态字段也可在声明时初始化。
- 和实例字段不同,未初始化的静态字段将获得默认值(0,null,false等),所以没有显式赋值的静态字段也是可以访问的
- 变量的作用域是可以不加限定来引用的程序代码区域
- 静态字段的作用域是类(及其任何派生类)
注意,在设计对象时,程序员要考虑字段和方法应声明为静态还是基于实例。
- 将不需要访问任何实例数据的方法声明为静态方法
- 将需要访问实例数据的方法(实例不作为参数传递)声明为实例方法
- 静态字段主要存储对应于类的数据,比如新实例的默认值或者已创建实例个数
- 实例字段主要存储和对象关联的数据
例如,我们需要记录到现在一共创建过多少个该对象的实例,就可以用静态字段。
什么是const(常量)
- 一个值不可以改变的静态字段
- 在编译时值就已经定下来了
- 任何使用常量的地方,编译器都会把这个常量替换为它的值
- 常量的类型可以是内置的数值类型、bool、char、string或enum
- 使用const关键字声明,生命的同时必须使用具体的值来对其初始化
示例:
public class Test
{
public const string Message = "Hello World!";
}
设计规范:
- 要为永远不变的值使用常量字段
- 不要为将来会发生变化的值使用常量字段
什么是readonly
- 和const不同,readonly修饰符只能用于字段,不能用于局部变量
- 它指出字段值只能从构造函数中更改,或在声明时通过初始化器指定
原来只读(readonly)的字段是可以修改的,不过只能从构造函数中更改,或在声明时通过初始化器指定。const的不可以修改。
常量(const)与静态只读字段的区别
- 常量比静态只读字段更严格:
- 可使用的类型(const更少一些)
- 字段初始化的语义上
- 常量是在编译时进行值的估算
常量编译时直接把具体的值带进来了,运行时直接运行具体的值。
注意:
当值有可能改变,并且与需要暴露给其它Assembly(程序集)的时候,静态只读(static、readonly)字段是相对较好的选择。
public const decimal ProgramVersion = 2.3;
如果Y Assembly引用了X Assembly并且使用了这个常量,那么在编译的时候,2.3这个值就会被固化于Y Assembly里。这意味着,如果后来X重编译了,这个常量变成了2.4或其它值,如果Y不重新编译的话,Y将仍然使用2.3这个值,直到Y被重新编译,它的值才会变成2.4.静态只读字段就会避免这个问题的发生
另外,方法可以有本地的常量:
static void Main()
{
const double twoPI = 2 * System.Math.PI;
...
}
什么是静态构造函数
-
静态构造函数,每个类执行一次
-
非静态构造函数,每个实例执行一次
-
一个类型只能定义一个静态构造函数
- 必须无参
- 方法名与类型一致
class Test { static Test() { Console.WriteLine("Type Initialized"); } }
-
在类型使用之前的一瞬间,编译器会自动调用类型的静态构造函数:
- 实例化一个类型
- 访问类型的一个静态成员
-
只允许使用 unsafe 和 extern 修饰符
注意,如果静态构造函数抛出了未处理的异常,那么这个类型在该程序的剩余生命周期内将无法使用了。
静态字段和静态构造函数的初始化顺序
- 静态字段的初始化器在静态构造函数被调用之前的一瞬间运行
- 如果类型没有静态构造函数,那么静态字段初始化器在类型被使用之前的一瞬间执行,或者在运行时突发奇想的时候执行
- 静态字段的初始化顺序与它们的声明顺序一致
class Foo
{
public static int X = Y; // 0
public static int Y = 3; // 3
}
例子:
class Program
{
static void Main() { Console.WriteLine(Foo.X); } // 3
}
class Foo
{
public static Foo Instance = new Foo();
public static int X = 3;
Foo() { Console.WriteLine(X); } // 0
}
Program中,使用了类型Foo,在类型使用之前的一瞬间,初始化了Foo类中第二行的静态字段X,所以Program中输出3。
Foo中,静态字段的初始化顺序使按照它们声明的顺序决定的,即先初始化Instance,new Foo(),直接调用了该构造函数,此时X还未被初始化为3,所以输出X的值为int型的默认值 0。
什么是静态属性
属性也可声明为static,使用静态属性几乎肯定比使用公共静态字段好,因为公共静态字段在任何地方都能调用,而静态属性至少提供了一定程度的封装。
什么是静态类
- 类也可以是静态的
- 其成员必须全是静态的
- 不可以有子类
- 例如
- System.Console
- System.Math
什么是Finalizer(终结器或析构函数)
- Finalizer是class专有的一种方法
- 在GC回收未引用对象的内存之前运行
- 其实就是对object的Finalize()方法重写的一种语法
class Class1
{
~Class1()
{
...
}
}
这个东西我学C++的时候好像翻译成析构函数,在开发中基本没用过。
感受
static和const是自从我开始学编程以来就一直困扰着我的东西,因为平常不怎么用,就一直没去研究。现在我依旧不是很理解它们。希望以后可以随着我的编程经验的增加,来慢慢理解它们。