2013-01-20 32 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不,我认为你应该尝试重新编写你的代码。如果在第一次迭代中有一部分应该跳过,请考虑将该部分移至循环的末尾。那么也许停止循环的标准需要移到循环块的中间。 –

相关问题