本来想写一个日志类,初始化是获取streamwriter,然后记录日志时write,flush,这样可以避免重复打开文件,可是streamwriter莫名其妙变成null了,于是写了一个简单的测试demo
using System;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Text;
namespace ConsoleApp1
{
class Tmp
{
public Tmp()
{
System.Console.WriteLine("tmp construct");
}
~Tmp()
{
System.Console.WriteLine("~tmp");
}
}
class Handle
{
private static Handle h = new Handle();
private static Tmp t = null;
private static int a = getA();
public Handle()
{
System.Console.WriteLine("Handle construct");
t = new Tmp();
}
public static Tmp getTmp()
{
return t;
}
public static int getA()
{
System.Console.WriteLine("getA");
return 1;
}
~Handle()
{
System.Console.WriteLine("~Handle");
}
}
class Program
{
static void Main(string[] args)
{
System.Console.WriteLine("Main");
if (null == Handle.getTmp())
{
System.Console.WriteLine("t is null");
}
else
{
System.Console.WriteLine("t not null");
}
System.Console.Read();
}
}
}
输出如下
Main
Handle construct
tmp construct
getA
t is null
System.Console.Read();生效前Tmp的构造函数发生了,但是没有经过析构函数就莫名其妙变成null,
Handle的构造函数发生在Main输出之后,此输出是Handle的静态变量h初始化时发生的,因为初始化是赋值为new Handle();
Tmp构造函数是在Handle的构造函数中调用的,唯一赋值null的地方在初始化时,
于是得出以下结论:
1.静态变量不是立即初始化,而是在第一次调用时初始化,因为Main输出在Handle construct之前,
2.静态变量按顺序初始化,如上打印,Handle construct在getA之前
3.静态变量一次初始化所有,如上并没有使用Handle的静态变量a,但是经过了a的初始化getA()
从以上三个结论可以再次推导出t为什么是null,
Handle的静态变量初始化时,Handle中将t也初始化了,
而第二个变量也就是t作为第二变量初始化为null,便与Handle构造函数中的赋值冲突,将t又重置为null了
也就是说,变量还是一开始都在的,只是都是空的,并没有初始化。这种情况下只需要把有使用其他变量的初始化函数放到最后面即可,或者干脆全部赋值为null,0;然后在需要初始化的地方初始化
private static Tmp t = null;
private static int a = getA();
private static Handle h = new Handle();
//private static Handle h = null;
换个顺序,t先被赋值为null,new Handle()发生时,Handle的构造函数中初始化时t被第二次赋值
于是输出:
Main
getA
Handle construct
tmp construct
t not null
最好变量定义时全部赋值为空,null,0,然后统一再次赋值或修改,这样就不会发生这种问题了,不过发生这种问题,然后把问题找出来也是个乐趣。