【问题标题】:Do I really have to give an initial value to all my variables? [duplicate]我真的必须给我的所有变量一个初始值吗? [复制]
【发布时间】:2013-01-03 13:57:23
【问题描述】:

可能重复:
Why do I have to assign a value to an int in C# when defaults to 0?

我刚开始通过编写一个名为 Journal 的人为应用程序来学习 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;
        }
    }
}

【问题讨论】:

  • 没有。但有效代码必须在所有局部变量被访问之前为其分配一个值,并且编译器必须保证这一点。我确定这是重复的。
  • @pst:那我怎么保证编译器的呢?
  • 这是编译器确保在变量有值之前不使用变量的方法。编译器无法从您复杂的条件语句中确定 currentEntryDate 在您使用它之前会有一个值,因此它会抛出该错误。在这里给currentEntryDate一个初始值是悲剧吗?
  • @pst:你为什么把这个问题选为与Why do I have to assign a value to an int in C# when defaults to 0 重复的问题?这是一个完全不同的问题,应该得到不同的答案。

标签: c#


【解决方案1】:

在这种情况下,编译器没有意识到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;
        }

【讨论】:

  • 那么你同意这是使用DateTime?的好时机吗?
  • @Codemonkey 不,我认为您应该尝试重新编写代码。如果有部分只应在第一次迭代中跳过,请考虑将此部分移到循环的末尾。那么可能需要将停止循环的标准移动到循环块的中间。
【解决方案2】:

我认为这里的问题是编译器不够聪明,无法掌握您读取输入的方式,并且存在变量不会被初始化的执行路径,即如果它通过else首先,在if 之前。为避免这种情况,您可能需要在定义时进行初始化。

【讨论】:

  • 所以换句话说,是的,我必须给变量一个永远不会被使用的初始值,只是为了让编译器满意?有没有其他方法可以解决这个问题?
  • @Codemonkey:不,您必须确保没有代码路径从未初始化的变量中读取。抛出异常也会让编译器满意。
  • @DanielPryden 如果bool hitFirstEntry 改变了它的值,它只能通过最里面的else 块(currentEntryDate 的读取发生)。但它只能在最里面的if 块中更改它的值,就在它上面。这是编译器不考虑的两个局部变量hitFirstEntrycurrentEntryDate的“纠缠”。这不取决于文件的内容。看我的回答。
【解决方案3】:

像这样重组你的循环怎么样?这将确保 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();
}

【讨论】:

  • 但是这段代码为文件中的每一行实例化了一个new DateTime...在我看来这比我抱怨的初始值还要浪费:P
  • @Codemonkey 不,它没有。它为每个条目的开头实例化一个DateTime,就像你的一样。恕我直言,这种方法更加简洁明了。
  • @Codemonkey: DateTime 是一个struct,所以它会在这里被堆栈分配。无论如何,分代 GC 中的分配与堆栈分配一样快,因此无需担心。
【解决方案4】:

如果你真的,真的不想实例化它:

DateTime? currentEntryDate = null

问号使 DateTime 可以为空,但通常不是。

【讨论】:

  • 在代码中,保证被赋予了一个有意义的值。编译器只是没有意识到程序流程不允许在初始化之前使用变量。
  • 我不同意。当我发现使用它比在每个路径上适当地分配(或者也许重构一个方法)更清洁时,几乎没有什么地方。但它“会编译”。 (如果存在“编译器不正确”的异常路径,那么它很可能是一个错误 - 抛出异常或以其他方式做出相应的反应。)
  • 我不确定。为它分配一个有意义的值将隐藏在变量实际未初始化时发生的错误。在使用它的值之前,我会使其可以为空并做出断言。
  • 如果选择此解决方案,则应删除 hitFirstEntry 变量。然后应该使用if (currentEntryDate.HasValue) 来检查是否不是第一次处理"--" 行。
【解决方案5】:

你声明了局部变量 此变量没有默认值并从堆栈中获取它们的内存 你应该在使用它们之前初始化局部变量 更改此代码行:

currentEntryDate = new DateTime();

你的代码行不通,

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-04
    • 2022-07-12
    • 2017-04-09
    • 2020-05-31
    • 1970-01-01
    • 1970-01-01
    • 2017-03-10
    • 1970-01-01
    相关资源
    最近更新 更多