none
为何 static 的类全局变量,执行static方法时,却显示未赋值? RRS feed

  • 问题

  • 先上图片

    如上图,我先简单介绍一下:

    1.ShortMessageFilePath 是类ShortMessage的全局静态变量,private的,

    2.ShortMessageFilePath 赋初值为“ms-appx:///Assets/ShortMessage.xml”,

    3.ShortMessageFilePath 除了赋初值以外,并未在其他地方(例如ShortMessage的构造方法中)赋过值,

    4.在静态方法GetShortMessages中使用ShortMessageFilePath 全局变量,GetShortMessages标志位异步(async),

    根据以上代码,我在GetShortMessages获取的ShortMessageFilePath 为null!!这怎么可能!!颠覆了我以前对静态字段的认识,ShortMessageFilePath 应该在调用GetShortMessages前就会被赋值啊,怎么可能调用了还不赋值?我单独做了个示例都是正常的。

    所以我想问问:

    1.静态私有字段赋初值时,会在什么时候执行赋初值?

    2.调用静态方法,和,类型中的静态字段赋初值,哪个先执行?还是顺序不固定?

    以下是有问题代码:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Threading.Tasks;
    using Windows.Storage;
    using System.Xml.Linq;
    using System.IO;
    
    namespace ShortMessageCollection
    {
        public class ShortMessage
        {
            // todo:此处赋初值
            private static string ShortMessageFilePath = "ms-appx:///Assets/ShortMessage.xml";
    
            public int Id { get; set; }
            public int Type { get; set; }
            public string Content { get; set; }
            public int WordCount { get { return this.Content.Length; } }
    
            public ShortMessage(int id, int type, string content)
            {
                this.Id = id;
                this.Type = type;
                this.Content = content;
            }
    
            public async static Task<List<ShortMessage>> GetShortMessages(int type, int pageIndex, int pageSize)
            {
                // todo:此处得到的ShortMessageFilePath 为null
                var uri = new Uri(ShortMessageFilePath);
                var file = await StorageFile.GetFileFromApplicationUriAsync(uri);
                using (var stream = await file.OpenReadAsync())
                {
                    using (var reader = new StreamReader(stream.AsStreamForRead()))
                    {
                        string xml = await reader.ReadToEndAsync();
                        var smsElement = XElement.Parse(xml);
                        return smsElement.Elements("sm").Select(p => new ShortMessage(int.Parse(p.Attribute("id").Value), int.Parse(p.Attribute("type").Value), p.Attribute("content").Value)).Skip(pageIndex * pageSize).Take(pageSize).ToList();
                    }
                }
            }
        }
    }
    

    2014年11月29日 4:21

答案

  • 我這幾天經過了一長串的測試, 發現一些有趣的現象
    (1)

    如果專案是 console, WPF, Windows Forms , Silverlight (APS.NET 和 ASP.NET MVC 我沒測, 不過我猜應該也是這一組)
    在 Debug Mode 下中斷觀察的結果會和預期相符, 也就是看得到值

    如果專案是 Windows Store App/Windows Phone App, 在 Debug Mode 下中斷觀察的結果和樓主所發的結果相同, 也就是其值會是 null

    (2)

    先來探討指派值給 field 的問題, 一個 static 的 field 何時會取得值 ? 我們假設以下的宣告.

     public class Class1
        {
            private static string x = "abc";
    }

    當這個 Class1 被呼叫時, 他會先建立靜態成員 x, 此時還不會指派 "abc" 給予 x, 必須等到 Class1 的靜態建構子被呼叫時, 才會指派 x="abc"

    上述的程式碼並沒有撰寫靜態建構子, 可是當你編譯這個程式的時候, C# 編譯器會自動幫你加上一個靜態建構子, 這點可以IL DASM 觀察出來, 類別內會包含一個 .cctor()
    內容大概如下所示:

    .method private hidebysig specialname rtspecialname static 
            void  .cctor() cil managed
    {
      // Code size       11 (0xb)
      .maxstack  8
      IL_0000:  ldstr      "abc"
      IL_0005:  stsfld     string StaticTest0.Class1::x
      IL_000a:  ret
    } // end of method Class1::.cctor
    

    (3) 弔詭的狀況就在這邊發生, 如果是 Console,WPF 等等的專案, 它在建立 type object 時就會呼叫此靜態建構子為靜態成員 x 賦值.

    但是在 Windows Store App/Windows Phone app 的狀態在建立 type object 只會建立成員變數, 而在當使用到某一個靜態成員時才會呼叫此靜態建構子為所有個靜態成員變數賦值.

    (4) 更奇特的現象是如果程式碼中有手動寫靜態建構子, 如:

     public class Class1
        {
            private static string x = "abc";
             static Class1()
            {
            }
         }

    他又會很神奇地在建立 type object 後馬上呼叫此靜態建構子. 這時在中斷點的觀察值是正確的.

    (5) 在 MSDN 文件中關於靜態建構子有提到 "使用者無法控制程式中靜態建構函式執行的時間", 也就是說它是由 CLR 來控制的, 由此推斷, Windows Store App/Windows Phone App 的 CLR 在對於呼叫靜態建構子的時機可能有修改, 導致它的行為和原有 Console, WPF, Windows Forms 等等專案的結果不同.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。


    2014年12月2日 15:32

全部回复

  • 想請問你調用的方式? 先回答您所問的兩個問題: 1. static物件再app一執行時就會被創立起來,這時候若您有給初值就回在這個時候進行賦值 2. 呈上,所以可以知道你再調用靜態方法前,靜態變數就已經被賦值了
    2014年11月29日 6:45
  • 靜態成員並非在 App 一被執行就會建立. 靜態成員的建立是在他所依附的 Type Object 建立時才會被建立的.

    以樓主的例子, ShortMessageFilePath 是附在 ShortMessage Class, 所以ShortMessageFilePath甚麼時候會出現 ?

    當 ShortMessage Class 的 Type Object 被建立時, 那 Type Object 何時被建立? 當第一次使用該 type 時, 例如, 使用 ShortMessage 中的靜態成員, 或是利用 ShortMessage 建立一個執行個體.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。


    2014年11月29日 10:24
  • 我這幾天經過了一長串的測試, 發現一些有趣的現象
    (1)

    如果專案是 console, WPF, Windows Forms , Silverlight (APS.NET 和 ASP.NET MVC 我沒測, 不過我猜應該也是這一組)
    在 Debug Mode 下中斷觀察的結果會和預期相符, 也就是看得到值

    如果專案是 Windows Store App/Windows Phone App, 在 Debug Mode 下中斷觀察的結果和樓主所發的結果相同, 也就是其值會是 null

    (2)

    先來探討指派值給 field 的問題, 一個 static 的 field 何時會取得值 ? 我們假設以下的宣告.

     public class Class1
        {
            private static string x = "abc";
    }

    當這個 Class1 被呼叫時, 他會先建立靜態成員 x, 此時還不會指派 "abc" 給予 x, 必須等到 Class1 的靜態建構子被呼叫時, 才會指派 x="abc"

    上述的程式碼並沒有撰寫靜態建構子, 可是當你編譯這個程式的時候, C# 編譯器會自動幫你加上一個靜態建構子, 這點可以IL DASM 觀察出來, 類別內會包含一個 .cctor()
    內容大概如下所示:

    .method private hidebysig specialname rtspecialname static 
            void  .cctor() cil managed
    {
      // Code size       11 (0xb)
      .maxstack  8
      IL_0000:  ldstr      "abc"
      IL_0005:  stsfld     string StaticTest0.Class1::x
      IL_000a:  ret
    } // end of method Class1::.cctor
    

    (3) 弔詭的狀況就在這邊發生, 如果是 Console,WPF 等等的專案, 它在建立 type object 時就會呼叫此靜態建構子為靜態成員 x 賦值.

    但是在 Windows Store App/Windows Phone app 的狀態在建立 type object 只會建立成員變數, 而在當使用到某一個靜態成員時才會呼叫此靜態建構子為所有個靜態成員變數賦值.

    (4) 更奇特的現象是如果程式碼中有手動寫靜態建構子, 如:

     public class Class1
        {
            private static string x = "abc";
             static Class1()
            {
            }
         }

    他又會很神奇地在建立 type object 後馬上呼叫此靜態建構子. 這時在中斷點的觀察值是正確的.

    (5) 在 MSDN 文件中關於靜態建構子有提到 "使用者無法控制程式中靜態建構函式執行的時間", 也就是說它是由 CLR 來控制的, 由此推斷, Windows Store App/Windows Phone App 的 CLR 在對於呼叫靜態建構子的時機可能有修改, 導致它的行為和原有 Console, WPF, Windows Forms 等等專案的結果不同.


    在現實生活中,你和誰在一起的確很重要,甚至能改變你的成長軌跡,決定你的人生成敗。 和什麼樣的人在一起,就會有什麼樣的人生。 和勤奮的人在一起,你不會懶惰; 和積極的人在一起,你不會消沈; 與智者同行,你會不同凡響; 與高人為伍,你能登上巔峰。


    2014年12月2日 15:32
  • 多谢,我也做了几个例子发现:

    1.console,WPF,winform,asp.net中的静态字段在调用静态方法时,都会先执行赋初值,应该是在类型初始化时就已经赋值,

    2.而对于windows store,windows phone store中的静态字段,在第一次使用字段时才会赋初值,所以通过断点监视静态字段的值时,很可能是default(T)的值,而在运行时获取(非断点监视)的值却不是default(T),

    2014年12月3日 7:38