• 【C#】静态构造方法与静态变量


    扯下闲篇先,本来今天预计整理下委托、事件、Lamada的笔记,然后再把单例模式的懒汉、饿汉模式看完。

    在看到懒汉的双重加锁设计时,向同桌贩卖了下该设计的优点,结果反被同桌的一个问题难倒了~!

    一. 有无静态构造方法,运行结果大不同

    问题:请问下面代码的运行结果是什么?

     1 using System;
     2 
     3 namespace FirstTest
     4 {
     5     class A
     6     {
     7         public static string name = GetName ("Hello World~!");
     8 
     9         public static string GetName (string str)
    10         {
    11             Console.WriteLine("初始化静态成员:{0}", str);
    12             return str;
    13         }
    14     }
    15 
    16     class Program
    17     {
    18         static void Main(string[] args)
    19         {
    20             Console.WriteLine ("输出第一句话");
    21             string temp = A.name;
    22             Console.WriteLine("输出静态成员:{0}", temp);
    23         }
    24     }
    25 }

    一开始,我认为是:1)输出第一句话 2)初始化静态成员 3)输出静态成员

    运行结果:

    结果出乎意料吧?

    为什么静态成员的初始化反而先于第一句话执行呢??

    正在我百思不得其解的时候,同桌在原代码的基础上稍加改动,又发给了我:

     1 using System;
     2 
     3 namespace FirstTest
     4 {
     5     class A
     6     {
     7         public static string name = GetName ("Hello World~!");
     8 
     9         public static string GetName (string str)
    10         {
    11             Console.WriteLine("初始化静态成员:{0}", str);
    12             return str;
    13         }
    14 
    15         // 在A类中增加其静态构造方法
    16         static A ()
    17         {
    18         }
    19     }
    20 
    21     class Program
    22     {
    23         static void Main(string[] args)
    24         {
    25             Console.WriteLine ("输出第一句话");
    26             string temp = A.name;
    27             Console.WriteLine("输出静态成员:{0}", temp);
    28         }
    29     }
    30 }

    这一次,运行结果和我刚刚预期的结果反而一致了。

    运行结果:

    为什么A类中有无静态构造方法,会对运行结果造成如此影响?

    在对度娘上的博客进行各种遍历搜索查询之后,终于捋清了如下思路

    1. 前后两段代码的异同点,仅局限于A类中有无静态构造方法。在缺少静态构造方法的情况下(即第一种情况),运行结果“异常”(不符合预期结果)。
    2. 在缺少静态构造方法的情况下,程序会自动生成默认静态构造方法

    由以上两点推测,较为认同如下观点

    • 编译器在编译的时候,会事先分析Main方法中所需要的静态字段
    • 如果这些静态字段所在的类有静态构造函数,则只有在初次引用该字段(或初次实例化类)的时候才进行初始化
    • 否则,在调用Main方法前,自动生成静态构造方法,对静态字段进行初始化

     继续码代码:增加一个class B,在Main方法中分别调用A、B的静态字段,印证观点

     1 using System;
     2 
     3 namespace FirstTest
     4 {
     5     class A
     6     {
     7         public static string name = GetName("Hello World~!");
     8 
     9         public static string GetName(string str)
    10         {
    11             Console.WriteLine("初始化静态成员:{0}", str);
    12             return str;
    13         }
    14 
    15         // 在A类中增加其静态构造方法
    16         static A()
    17         {
    18         }
    19     }
    20     class B
    21     {
    22         public static string name = GetName("Learning C#");
    23 
    24         public static string GetName(string str)
    25         {
    26             Console.WriteLine("初始化静态成员:{0}", str);
    27             return str;
    28         }
    29 
    30         // B类中缺少静态构造方法,将由系统自动生成
    31     }
    32     class Program
    33     {
    34         static void Main(string[] args)
    35         {
    36             Console.WriteLine("开始执行Main方法");
    37             // 首先引用A类的静态字段
    38             string first  = A.name; 
    39             // 其次引用B类的静态字段
    40             string second = B.name;
    41         }
    42     }
    43 }

    按照观点分析:1)初始化静态成员:Learning C# 2)开始执行Main方法 3)初始化执行静态成员:Hello World~!

    运行结果:

    与观点分析的预期相符,结题。

    参考博文:

    《浅谈静态字段与静态构造函数之间的初始化关系以及执行顺序》

    作者:Rain

    http://www.cnblogs.com/cpcpc/archive/2010/04/16/2123135.html

    二. 静态构造方法与静态字段的执行顺序

    在查阅上个问题的过程中,发现在MSDN上对静态构造方法的描述中有如下一条内容:

    如果类包含任何带有初始值设定项的静态字段,则在执行该类的静态构造函数时,先要按照文本顺序执行那些初始值设定项

    观点主要阐述了以下3点:

    • 执行静态构造方法时,先对静态字段进行初始化,然后再调用静态构造方法里面的内容
    • 对静态字段进行初始化时,按照静态字段声明的依次顺序进行
    • 若静态字段含有初始值含有初始值设定项,则使用设定项。否则对其使用默认值,值类型的默认值为0(其中布尔类型为false),引用类型的默认值为null。

    本着打破砂锅问到底的精神,仅在A类中添加如下两行静态字段,在静态构造函数中添加一行输出语句,进行测试:

     1 class A
     2 {
     3     public static string front = GetName("前排强势占座!!!!");
     4     public static string name = GetName("Hello World~!");
     5     public static string back = GetName("后排无聊围观....");
     6 
     7     public static string GetName(string str)
     8     {
     9         Console.WriteLine("初始化静态成员:{0}", str);
    10         return str;
    11     }
    12 
    13     // 在静态构造方法增加输出语句
    14     static A()
    15     {
    16         Console.WriteLine("调用静态构造方法");
    17     }
    18 }

    按照MSDN的观点:1)初始化静态成员:Learning C# 2)开始执行Main方法 3)初始化执行静态成员:前排强势占座!!!! 4)初始化执行静态成员:Hello World~! 5)初始化执行静态成员:后排无聊围观.... 6)调用静态构造方法

    运行结果:

    与观点分析的预期相符,新姿势Get√

    另外,根据MSDN中的静态构造函数设计中的描述,CLR可以优化对静态字段进行初始值设定项的代码。

    三. 静态构造方法与Main方法的执行顺序

    同样的,在MSDN中发现如下观点:

    如果类中包含用来开始执行的 Main 方法,则该类的静态构造函数将在调用 Main 方法之前执行。

    重写一份代码(含有静态构造方法),再次实验下:

     1 using System;
     2 
     3 namespace FourthText
     4 {
     5     class Program
     6     {
     7         static void Main(string[] args)
     8         {
     9             Console.WriteLine ("这是最后一个试验了~\(≧▽≦)/~啦啦啦");
    10         }
    11 
    12         static Program()
    13         {
    14             Console.WriteLine ("调用静态构造方法");
    15         }
    16     }
    17 }

    运行结果:

    与观点分析的预期相符,新姿势Get√

    啊啊啊啊O(≧口≦)O,还不能结题~!结合第一、二条知识,再次测试下没有静态构造方法下的情况吧:

     1 using System;
     2 
     3 namespace FourthText
     4 {
     5     class Program
     6     {
     7         static void Main(string[] args)
     8         {
     9             Console.WriteLine("真的是最后一次试验了Zzz");
    10         }
    11 
    12         public static string name = GetName("又被骗了(#‵′)凸");
    13 
    14         public static string GetName(string str)
    15         {
    16             Console.WriteLine("初始化静态成员:{0}", str);
    17             return str;
    18         }
    19     }
    20 }

    运行结果:

  • 相关阅读:
    轮播图
    .Net语言 APP开发平台——Smobiler学习日志:如何快速实现快递信息流的效果
    .Net语言 APP开发平台——Smobiler学习日志:如何实现离线声音文件上传
    .Net语言 APP开发平台——Smobiler学习日志:Poplist控件的正确打开方式以及如何快速实现
    .Net语言 APP开发平台——Smobiler学习日志:快速实现手机上的图片上传功能
    .Net语言 APP开发平台——Smobiler学习日志:快速在手机上实现n×m形式的菜单(IconMenuView)
    .Net语言 APP开发平台——Smobiler学习日志:快速在手机上实现日历功能
    .Net语言 APP开发平台——Smobiler学习日志:快速实现手机上常见的GridView
    .Net语言 APP开发平台——Smobiler学习日志:实现在手机上调用摄像头进行扫描
    .Net语言 APP开发平台——Smobiler学习日志:实现手机上常见的ListMenuView
  • 原文地址:https://www.cnblogs.com/muxiaomo/p/4483730.html
Copyright © 2020-2023  润新知