2013-01-20 73 views
-1

可能重複:
Why do I have to assign a value to an int in C# when defaults to 0?我真的必須給我的所有變量初始值嗎?

我剛開始寫所謂的雜誌一個人爲的應用學習C#。在解析日誌文件的函數中,我聲明瞭變量DateTime currentEntryDate。直到達到定義新條目的行時,它纔會獲得價值。 當我到達輸入行時,該變量將用於爲上一個條目創建類JournalEntry的實例。

的問題是,對於變量的使用代碼將無法編譯:

使用未分配的局部變量的「currentEntryDate」

這是沒有意義的我。爲了保持編譯器的快樂,我真的必須爲變量賦予一個浪費的初始值嗎?當然,我誤解了某些東西,或者在我的代碼中存在錯誤。

Pastebin上的代碼:Journal.cs。我已經強調了相關的路線。

代碼:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Text.RegularExpressions; 
using System.IO; 

namespace Journal 
{ 
    class Journal 
    { 
     public List<JournalEntry> Entries; 

     private static readonly string EntryLineRegex = 
      @"-- Entry: (?<title>.*) \((?<year>\d{4})-(?<month>\d{2})" + 
      @"-(?<day>\d{2})\)"; 

     public static Journal FromFile(string filePath) 
     { 
      Journal returnValue = new Journal(); 

      StreamReader fileReader = new StreamReader(filePath); 

      // Prepare variables for parsing the journal file. 
      bool hitFirstEntry = false; 
      DateTime currentEntryDate; 
      string currentEntryTitle; 
      StringBuilder currentEntryText = new StringBuilder(); 

      // Prepare a regular expression for the entry lines. 
      Regex entryLineRegex = new Regex(EntryLineRegex); 

      while (!fileReader.EndOfStream) 
      { 
       string line = fileReader.ReadLine(); 

       if (line.StartsWith("--")) 
       { 
        // Is this the first entry encountered? If so, don't try to 
        // process the previous entry. 
        if (!hitFirstEntry) 
        { 
         hitFirstEntry = true; 
        } 
        else 
        { 
         // Create a JournalEntry with the current entry, then 
         // reset for the next entry. 
         returnValue.Entries.Add(
          new JournalEntry(
           currentEntryText.ToString(), currentEntryDate 
          ) 
         ); 

         currentEntryDate = new DateTime(); 
         currentEntryText.Clear(); 
        } 

        // Extract the new entry title and date from this line and 
        // save them. 
        Match entryMatch = entryLineRegex.Match(line); 
        GroupCollection matches = entryMatch.Groups; 

        currentEntryDate = new DateTime(
         Convert.ToInt16(matches["year"].Value), 
         Convert.ToInt16(matches["month"].Value), 
         Convert.ToInt16(matches["day"].Value) 
        ); 

        currentEntryTitle = matches["title"].Value; 
       } 
       else 
       { 
        currentEntryText.Append(line); 
       } 
      } 

      return returnValue; 
     } 
    } 

    class JournalEntry 
    { 
     public string Text; 
     public DateTime EntryDate; 

     public JournalEntry(string text, DateTime entryDate) 
     { 
      this.Text = text; 
      this.EntryDate = entryDate; 
     } 
    } 
} 
+3

不可以,但有效的代碼*必須*分配所有本地變量被訪問前值和編譯器*必須*保證這個的。我確信這是重複的。 – 2013-01-20 08:45:34

+0

@pst:我如何保證編譯器的? – Hubro

+0

這是編譯器確保在變量沒有值之前不使用變量的方法。編譯器無法從你複雜的條件語句中判斷出'currentEntryDate'在你使用它之前會有一個值,所以它會拋出錯誤。在這裏爲'currentEntryDate'賦予一個初始值如此悲劇? – JLRishe

回答

1

如果你真的,真的不希望實例吧:

DateTime? currentEntryDate = null 

問號使得日期時間爲空的,它通常是不。

+2

在代碼中,它保證被分配一個有意義的值。編譯器並不完全意識到程序流將不允許在初始化之前使用該變量。 –

+1

我不同意。當我發現使用它比每個路徑適當地分配(或者重構一個方法)更乾淨的時候,很少有這樣的地方。但它「會編譯」。 (如果出現「編譯器不正確」的偏離路徑,那麼它應該可能是一個錯誤 - 拋出一個異常或相應地作出反應。) – 2013-01-20 09:01:54

+1

我不確定。當變量實際使用未初始化時,爲其分配一個有意義的值將隱藏發生的錯誤。在使用它的價值之前,我會讓它可以爲空並做出斷言。 –

2

我覺得這裏的問題是編譯器沒有足夠的智慧把握讀取輸入自己的方式,並且有執行的路徑,該變量將不初始化,即如果它首先經過else,在if之前。爲了避免這種情況,您可能需要在定義它時進行初始化。

+0

換句話說,是的,我必須給變量一個初始值,永遠不會使用它來保持編譯器的快樂。有沒有其他方法可以解決這個問題? – Hubro

+1

@Codemonkey:不,您只需確保沒有代碼路徑從未初始化的變量中讀取。拋出一個異常也會使編譯器滿意。 –

+0

@DanielPryden如果'bool' hitFirstEntry'已經改變了它的值,它就只能通過最內層的'else'塊(currentEntryDate的讀取發生)。但它只能在最上面的if區塊中改變它的值。它是編譯器不考慮的** 2 **局部變量'hitFirstEntry'和'currentEntryDate'的「糾纏」。這不依賴於文件的內容。看到我的答案。 –

-2

您聲明局部變量 這個變量不具有默認值,從堆棧 拿自己的內存,你應在初始局部變量befor與此代碼的變線期運用他們 :

currentEntryDate =新的DateTime();

太上線你的代碼會工作,

2

這樣重構你的循環如何?這將確保currentEntryDate有一個值,你使用它之前:

string line = fileReader.ReadLine(); 
while (line != null) 
{ 
    // Extract the new entry title and date from this line and 
    // save them. 
    Match entryMatch = entryLineRegex.Match(line); 
    GroupCollection matches = entryMatch.Groups; 

    currentEntryDate = new DateTime(
     Convert.ToInt16(matches["year"].Value), 
     Convert.ToInt16(matches["month"].Value), 
     Convert.ToInt16(matches["day"].Value) 
    ); 

    currentEntryTitle = matches["title"].Value; 

    while ((line = fileReader.ReadLine()) != null && !line.StartsWith("--")) 
    { 
     currentEntryText.Append(line); 
    } 

    // Create a JournalEntry with the current entry, then 
    // reset for the next entry. 
    returnValue.Entries.Add(
     new JournalEntry(
      currentEntryText.ToString(), currentEntryDate 
     ) 
    ); 

    currentEntryText.Clear(); 
} 
+0

但是這段代碼爲文件中的每一行實例化一個'new DateTime' ...這似乎比我抱怨的初始值更浪費:P – Hubro

+1

@Codemonkey不,它沒有。它爲每個條目的開始實例化一個DateTime,就像你的一樣。這種方法是相當清潔和簡單恕我直言。 – JLRishe

+0

@Codemonkey:'DateTime'是一個'struct',所以它會在這裏堆棧分配。無論如何,分代GC的分配速度與堆棧分配一樣快,所以沒有什麼可擔心的。 –

2

在這種情況下,編譯器不認識的hitFirstEntrycurrentEntryDate之間的「關係」。

即使你能「證明」,每當hitFirstEntry改爲true,那麼currentEntryDate即將之後分配的,currentEntryDate不會首次直到(最早)的在循環的下一次迭代中,編譯器不是複雜。也許你可以重新編寫你的代碼。

編輯:這是你的代碼的「最小」版本:

 bool isFirstTime = true; 
     DateTime localVarToBeAssigned; 

     while (true) 
     { 
      if (isFirstTime) 
      { 
       isFirstTime = false; 
      } 
      else 
      { 
       // this reads the variable 
       Console.WriteLine(localVarToBeAssigned); 
      } 

      // this assigns the variable 
      localVarToBeAssigned = DateTime.Now; 
     } 
+0

所以你會同意這是使用'DateTime?'的好時機嗎? – Hubro

+0

@Codemonkey不,我認爲你應該嘗試重新編寫你的代碼。如果在第一次迭代中有一部分應該跳過,請考慮將該部分移至循環的末尾。那麼也許停止循環的標準需要移到循環塊的中間。 –

相關問題