博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
对Static 类的认识
阅读量:5880 次
发布时间:2019-06-19

本文共 4859 字,大约阅读时间需要 16 分钟。

在学习 Design&Pattern 的 Singleton Pattern 的时候,因为静态初始化是在 .NET 中实现 Singleton 的首选方法.

 
1
public
sealed
class
Singleton
2
{
3
static
readonly
Singleton instance
=
new
Singleton();
4
5
static
Singleton()
6
{
7
}
8
9
Singleton()
10
{
11
}
12
13
public
static
Singleton Instance
14
{
15
get
16
{
17
return
instance;
18
}
19
}
20
}

不过我对于静态构造函数还是有点迷惑!它和实例化对象时的构造函数有什么区别,它们的关系又是什么呢?

查看了相关资料后现在对它有了一定的认识了!
静态成员的初始化语句会早于静态构造函数执行,其次静态构造函数是由CLR调用执行的,所以静态构造函数只能是一个,同时不能还有参数。那么静态构造函数相对于成员初始化的一些弊端都不复存在。
对于静态成员何时被初始化,静态构造函数又何时被调用,它们之间的先后顺序又是怎么样的呢?下面我们给出一个例子来说明一下!

 
1
using
System;
2
3
 
namespace
testStatic
4
{
5
class
Program
6
{
7
static
void
Main(
string
[] args)
8
{
9
Console.WriteLine(A.X.ToString());
//
Return: 2
10
 
Console.WriteLine(B.Y.ToString());
//
Return: 1
11
 
12
Console.Read();
13
}
14
}
15
16
class
A
17
{
18
public
static
int
X
=
B.Y;
19
20
static
A()
21
{
22
++
X;
23
}
24
}
25
class
B
26
{
27
public
static
int
Y
=
A.X;
28
29
static
B()
30
{
31
++
Y;
32
}
33
}
34
}

说实话我在以前对于 static 的认识的基础上我真的不知道上面的会返回什么值!(做人得诚实,不懂不能装懂!呵呵...).其结果是“2,1”,也就是

A.X的值为2,
B.Y的值为1.
分析此类问题,只要记住三点就行了:
1. 代码的执行顺序,代码在前的先执行;
2. 静态成员初始化语句要先于静态构造函数执行;
3. 静态成员初始化语句与静态构造函数只执行一次。

如果了解这三点,接下来就分析为什么会出现上面的结果。

当调用到第一条语句的时候:
Console.WriteLine(A.X.ToString());
     首先是访问A这个类型,那么要对A这个类型的静态成员进行初始化,其次如果有静态构造函数,需要调用它。对于A的静态成员只有“X”,按照上一单元的过程,先给其分配空间,并辅以0来初始化,其次调用其对应的成员初始化语句来初始化这个静态成员。那么它的成员初始化语句是“X = B.Y”,那么需要访问“B.Y”来初始化X这个静态成员。
    对于“B.Y”的访问,就是访问B类型,也是和访问A一样,首先对这个类型的静态成员进行初始化,其次如果有静态构造函数,需要调用它。而B的静态成员只有“Y”,先给其分配空间,并辅以0来初始化,其次调用其对应的成员初始化语句来初始化这个静态成员。
    那么对于“Y = A.X”成员初始化语句,由于此时不是第一次访问类型A,所以不再进行静态成员初始化和静态构造函数的调用,对于“A.X”的访问是直接访问。此时“A.X”的值为0,那么Y的值也为0;接着执行B的静态构造函数,这样处理后Y的值为1。那么对于A中的成员初始化语句“X = B.Y”,到此就执行完了,此时A类型中的X与B类型中的Y都是一样的,值为1。不过B中的静态成员初始化语句和静态构造函数都执行过了,而A中的静态构造函数还未执行。因此经过A的静态构造函数处理,A的X值为2,这也就是最后显示的结果。

为了再加深一下对 Static 的印象,再个小的测试片断!这下就对 Static 印象更深了~~`

 
1
using
System;
2
 
using
System.Collections.Generic;
3
using
System.Text;
4
using
System.Threading;
5
6
namespace
testStatic
7
{
8
class
Program
9
{
10
static
void
Main(
string
[] args)
11
{
12
//
Console.WriteLine(A.X.ToString());
13
//
Console.WriteLine(B.Y.ToString());
14
A objA
=
A.Instance;
15
16
Console.WriteLine(objA.GetStr());
17
18
Console.Read();
19
}
20
}
21
22
class
A
23
{
24
static
A instance
=
null
;
25
26
string
str
=
"
i am A, i am a member of class A
"
;
27
28
public
static
int
X
=
B.Y;
29
30
///
<summary>
31
///
静态构造函数仅次于静态数据成员被调用.
32
///
</summary>
33
static
A()
34
{
35
++
X;
36
Console.WriteLine(
"
hello A. I am from static A constructor
"
);
37
}
38
private
A()
39
{
40
Console.WriteLine(
"
hello A. I am from private A constructor
"
);
41
}
42
43
public
string
GetStr()
44
{
45
return
str;
46
}
47
48
public
static
A Instance
49
{
50
get
51
{
52
//
这里只有对类进行实例化的时候,非静态的构造函数才会被调用
53
return
instance
=
new
A();
54
}
55
}
56
57
58
}
59
class
B
60
{
61
//
静态成页最先被调用
62
public
static
int
Y
=
A.X;
63
///
<summary>
64
///
静态构造函数后于静态成员被调用
65
///
</summary>
66
static
B()
67
{
68
++
Y;
69
Console.WriteLine(
"
hello B . I am from static B constructor
"
);
70
}
71
72
73
///
<summary>
74
///
只有对象被实例化的时候才会调用非静态构造函数
75
///
</summary>
76
private
B()
77
{
78
Console.WriteLine(
"
hello B . I am from private B constructor
"
);
79
}
80
}
81
}

输出为:
hello B . I am from static B constructor
hello A. I am from static A constructor
hello A. I am from private A constructor
i am A, i am a member of class A

先看个例子:

 
1
using
System;
2
3
Class A
4
{
5
static
int
X;
6
static
A()
7
{
8
X
=
B.Y
+
1
9
}
10
}
11
12
Class B
13
{
14
public
static
int
Y
=
A.X
+
1
;
15
static
B() {}
16
17
static
void
Main()
18
{
19
Console.WriteLine(
"
X={0},Y={1}
"
,A.X,B.Y);
20
}
21
22
}

执行结果是X=2,Y=1

这个例子主要考查2个方面,一是static的用法,二是static的初始化顺序。了解了static的初始化顺序和规则,这个问题答答案就很容易理解了。这里涉及到以下三类static对象:static成员,static方法,static构造函数。规则如下:

一个类的static构造函数在给定的应用程序域中仅执行一次。static构造函数由在应用程序域的下列事件的首次发生时触发:

1)该类的实例被创建。
2)任何一个static成员被引用
3)如果类包含执行入口Main方法,此类的static构造函数在Main方法被调用之前执行。
4)如果类包含任何staic成员,则这些static成员在static构造函数之前进行初始化。
5)如果类包含任何static方法,则这些static方法在static构造函数之后进行初始化。
6)对于存在多个static成员,他们的初始化将按照文本顺序进行,不会因为调用顺序而改变。

现在看看上面的应用程序,Class B中有个Main执行入口,所以B首先得到初始化,顺序是static成员Y->static构造函数。在初始化Y时,引用了A.X,编译器又开始初始化Class A(注意这时Class B的初始化并没有完成),顺序也是static成员X->static构造函数。Class A中X在定义的时候没有被赋予初始值(在定义static变量时,尽量赋予初始值),编译器会默认赋予值0(int型)。然后再执行static的构造函数,由于Class B的初始化这时还没有完成,所以B.Y的值在这时被编译器赋予默认值0,所以在A的static的构造函数执行完后,X的值变为1,然后返回B继续完成初始化,得到Y的值为2。最后执行Main,输出A.X和B.Y的值。相反地,如果将B中的Main方法移出放在一个类C中,那执行结果又是什么呢?依据以上的规则,可以很方便的得出答案。

以下这题可以用来说明规则六,有兴趣的朋友可以思考一下答案

 

 
class
Class1
{
private
static
Class1 obj
=
new
Class1();
public
static
int
counter1;
public
static
int
counter2
=
0
;
private
Class1()
{
counter1
++
;
counter2
++
;
}
public
static
Class1 getInstance()
{
return
obj;
}
[STAThread]
static
void
Main(
string
[] args)
{
Class1 obj
=
Class1.getInstance();
Console.WriteLine(
"
Class1.counter1==
"
+
Class1.counter1);
Console.WriteLine(
"
Class1.counter2==
"
+
Class1.counter2);
}
}

转载地址:http://aodix.baihongyu.com/

你可能感兴趣的文章
GNS关于IPS&ASA&PIX&Junos的配置
查看>>
七天学会ASP.NET MVC (四)——用户授权认证问题
查看>>
upgrade to iOS7,how to remove stroyboard?
查看>>
影响企业信息化成败的几点因素
查看>>
SCCM 2016 配置管理系列(Part8)
查看>>
zabbix监控部署
查看>>
struts中的xwork源码下载地址
查看>>
Android硬件抽象层(HAL)深入剖析(二)
查看>>
CDays–4 习题一至四及相关内容解析。
查看>>
L3.十一.匿名函数和map方法
查看>>
java面向对象高级分层实例_实体类
查看>>
android aapt 用法 -- ApkReader
查看>>
[翻译]用 Puppet 搭建易管理的服务器基础架构(3)
查看>>
Android -- AudioPlayer
查看>>
Python大数据依赖包安装
查看>>
Android View.onMeasure方法的理解
查看>>
Node.js 爬虫初探
查看>>
ABP理论学习之仓储
查看>>
NestJS 脑图
查看>>
我的友情链接
查看>>