论坛首页 编程语言技术论坛

C#静态变量的诡异与恶心

浏览 28344 次
精华帖 (0) :: 良好帖 (2) :: 新手帖 (2) :: 隐藏帖 (2)
作者 正文
   发表时间:2008-12-27   最后修改:2008-12-28
发现一段很诡异的C#代码,见识了静态构造函数这种奇怪的东西:

using System;

namespace StaticTest
{
	class A
	{
		public static int X;
		static A()
		{
			X = B.Y + 1;
		}
	}
	class B
	{
		public static int Y = A.X + 1;
		static B()
		{
		}
		static void Main()
		{
			Console.WriteLine("X={0}, Y={1}", A.X, B.Y);
		}
	}
}


先补习下吧:

  1、静态构造函数既没有访问修饰符,也没有参数。因为是.NET调用的,所以像public和private等修饰符就没有意义了。
  
  2、是在创建第一个类实例或任何静态成员被引用时,.NET将自动调用静态构造函数来初始化类,也就是说我们无法直接调用静态构造函数,也就无法控制什么时候执行静态构造函数了。

  3、一个类只能有一个静态构造函数。

  4、无参数的构造函数可以与静态构造函数共存。尽管参数列表相同,但一个属于类,一个属于实例,所以不会冲突。

  5、最多只运行一次。

  6、静态构造函数不可以被继承。

  7、如果没有写静态构造函数,而类中包含带有初始值设定的静态成员,那么编译器会自动生成默认的静态构造函数。

好,复习完毕,哪位同学回答下上面程序的输出结果是多少?

还是搞不明白吧:) 唔,你明白了?他没明白,我也没明白……

class A静态构造函数中的B.Y好像很奇怪,貌似要确定A.X的值,得先确定B.Y的值,而B.Y的值在B中却是由A.X来确定的,那A.X的值……啊,要疯掉了……应该是多少呢?不敢确定了吧,交给编译器来运行看看吧~~
结果如下:
X=1, Y=2


修改下代码,看看内部到底怎么运行的:
using System;

namespace StaticTest
{
	class A
	{
		public static int X;
		static A()
		{
			Console.WriteLine("calling A");
			Console.WriteLine(B.Y);
			X = B.Y + 1;
		}
	}
	class B
	{
		public static int Y = A.X + 1;
		static B()
		{
			Console.WriteLine("calling B");
			Console.WriteLine(Y);
		}
		static void Main()
		{
			Console.WriteLine("X={0}, Y={1}", A.X, B.Y);
		}
	}
}

执行结果如下:
calling A
0
calling B
2
X=1, Y=2

看到这个结果,对C#更加迷惑了。类A中静态构造函数调用得到的B.Y,居然是0。这是什么道理?难道这个时候B还没出生,但是B.Y已经出生了?不管了,先接受了吧,以后玩C#还是小心点了。哪位神仙指点指点迷津啊?



   发表时间:2008-12-27  
这样看来,貌似是顺序执行了。呵呵。
0 请登录后投票
   发表时间:2008-12-27  
抛开语言不说,这样的交叉引用本身就是一个设计的bug、

为什么这样子,估计得看看IL或是C#规范
0 请登录后投票
   发表时间:2008-12-27  
查看与此联系人的全部对话记录
 
Iris 说:

Kimm King(秦风意动) 说:
48$01$
Iris 说:
下上午好
 
Kimm King(秦风意动) 说:
http://www.iteye.com/topic/305583
Kimm King(秦风意动) 说:
看看这个
Kimm King(秦风意动) 说:
静态构造函数
Kimm King(秦风意动) 说:
很诡异
Iris 说:
怎么了嘛
Iris 说:
这个很奇怪吗?
Kimm King(秦风意动) 说:
挺奇怪的 我找个reflector看看IL
Kimm King(秦风意动) 说:
不能理解
Iris 说:
静态构造函数的运行顺序IL里面看不出来的
Iris 说:
哥哥死心吧~~
Iris 说:
不过是一个 beforefieldsinit 元数据标志而已
Kimm King(秦风意动) 说:
B.Y为什么是0呢?
Kimm King(秦风意动) 说:
在A里调用时,
Iris 说:
等下哦,樱桃把语言规范中的一些资料给哥哥
Kimm King(秦风意动) 说:
ok~
Iris 说:
仅仅从代码本身来说
Iris 说:
哥哥能解释这个现象木
Iris 说:

Iris 说:
若要初始化新的封闭式类类型,需要先为该特定的封闭类型创建一组新的静态字段(第 10.5.1 节)。将其中的每个静态字段初始化为默认值(第 5.2 节)。下一步,为这些静态字段执行静态字段初始值设定项(第 10.4.5.1 节)。最后,执行静态构造函数。
Iris 说:
明白了吧
Iris 说:
IL在构造类的时候,字段都是会自动设置为默认值的
Iris 说:
呃……应该是CLR构造类的时候
Iris 说:
参看语言规范484页
Iris 说:
例子和给的代码一模一样
Iris 说:
有一段详细的解释
Kimm King(秦风意动) 说:
如果说 类被加载时 x和y都是0.
Iris 说:
要执行 Main 方法,系统在运行类 B 的静态构造函数之前首先要运行 B.Y 的初始值设定项。因为引用了 A.X 的值,所以 Y 的初始值设定项导致运行 A 的静态构造函数。这样,A 的静态构造函数将继续计算 X 的值,从而获取 Y 的默认值 0,而 A.X 被初始化为 1。这样就完成了运行 A 的静态字段初始值设定项和静态构造函数的进程,控制返回到 Y 的初始值的计算,计算结果变为 2。
Kimm King(秦风意动) 说:
发给我一份 规范
Iris 说:
VS安装目录由
Iris 说:
VS/VC#/Specifications/2052/
Kimm King(秦风意动) 说:
thanks
Iris 说:
要看过语言规范才能算深入理解^^
Kimm King(秦风意动) 说:
明白了,我也正奇怪,main在B中有没有影响
Iris 说:
是这样的
0 请登录后投票
   发表时间:2008-12-27  
如果类型存在静态的交叉引用
那么交叉链的单向终点
所有前向的被交叉对象
都为以默认值处理
0 请登录后投票
   发表时间:2008-12-27  
这种静态构造函数的设计,太霸道了,霸道得不可理喻。就跟kimmking所说,跟bug无异。我估计引入静态构造函数,是因为在实现解释器的时候发现了一个设计缺陷,但又很难避免,便强制规定在循环调用的第二次获取静态变量的默认值,以防止陷入死循环一样的状态。而,语言本身对静态成员的描述是,在每个类的静态变量成员调用之前,静态构造函数已经执行过一次,且最多执行一次。貌似就算C#语法上隐藏静态构造函数,实际上内部还是有这么一个东西存在,蠢蠢欲动以初始化静态构造。
对C#有点失望,我只是说C#,并非指.net或者mono,难怪微软要指定F#为新的一等公民……
0 请登录后投票
   发表时间:2008-12-27  
莫名其妙。好吧我跟你讲,你的代码在Java里面也是输出同样的结果,于是你就继续恶心吧。
0 请登录后投票
   发表时间:2008-12-27  
treenode 写道

莫名其妙。好吧我跟你讲,你的代码在Java里面也是输出同样的结果,于是你就继续恶心吧。


写了段Java的代码:
class StaticTest
{
	public static void main(String[] args){
		System.out.println(Integer.toString(A.X) +" "+Integer.toString(B.Y));
	}
}

class A
{
	static int X = B.Y + 1;
}

class B
{
	static int Y = A.X + 1;
}

treenode你说的一点没错,既然你不觉得恶心,那你知道为什么吗?或者内部的机理
0 请登录后投票
   发表时间:2008-12-27  
应该不是由于Java也有类似的“缺陷”,就觉得习以为常了吧?我倒觉得如果C++也这样,那就更奇怪了……阿弥陀佛,幸好不是
1 请登录后投票
   发表时间:2008-12-27  
你的问题就是不理解

引用
难道这个时候B还没出生,但是B.Y已经出生了


实际上编译器载入一个类的时候分两个步骤,1.将类加载到内存堆的某个位置,这个时候所有静态成员的值都是0或者null;2.如果该类定义了静态构造方法,则执行该方法。当你调用A的静态方法时,实际上编译器已经完成了第一步的动作,而第二步还没有完成,所以成员的值仍然是0。很正常的现象,有什么好诡异的?

你说C++不是,但不知道你说的是哪个C++编译器,我刚才用TC++试了试(现在手上只有这个),结果一样是1,2。
0 请登录后投票
论坛首页 编程语言技术版

跳转论坛:
Global site tag (gtag.js) - Google Analytics