为什么我的 getter 在出现 StackOverflow 之前一直被调用?

Why is my getter being called until there is a StackOverflow?

我在我的 Winforms 应用程序中使用了一个通用列表,并且在整个代码中我首先检查底层 (json) 文件是否存在,如果存在,反序列化它,然后访问反序列化的通用列表。我决定将代码放在一个地方会更好,所以这样做:

public static List<AssignmentHistory> assignmentHistList
{
    get { return GetAssignmentHistoryList(); }
}

public static List<AssignmentHistory> GetAssignmentHistoryList()
{
    if (!System.IO.File.Exists(ASSIGNMENT_HISTORY_FILENAME)) return null;
    if (null == assignmentHistList)
    {
        return DeserializeAssignmentHistFile();
    }
    return assignmentHistList;
}

public static List<AssignmentHistory> DeserializeAssignmentHistFile()
{
    var assignmentHistFile = System.IO.File.ReadAllText(ASSIGNMENT_HISTORY_FILENAME);
    var assignmentHistDeserialized = JsonConvert.DeserializeObject<List<AssignmentHistory>>(assignmentHist);
    return assignmentHistDeserialized;
}

然后我这样称呼它:

AssignmentHistory ah =
    AYttFMConstsAndUtils.assignmentHistList.FirstOrDefault(
        i => i.WeekOfAssignment == currentWeek && i.TalkType == 1);

...但永远不要超出那条线,因为 GetAssignmentHistoryList() 会被一遍又一遍地调用,直到出现堆栈溢出。我在这里做错了什么?

更新

我使用了 abto 的 null 合并运算符建议,但为了防止空文件的可能性,我还必须修改我的反序列化方法,现​​在是:

private static List<AssignmentHistory> DeserializeAssignmentHistFile()
{
    List<AssignmentHistory> assignmentHistoryList;
    if (!System.IO.File.Exists(ASSIGNMENT_HISTORY_FILENAME))
    {
        var assignmentFile = System.IO.File.Create(ASSIGNMENT_HISTORY_FILENAME);
        assignmentFile.Close();
    }

    var assignmentHistFile = System.IO.File.ReadAllText(ASSIGNMENT_HISTORY_FILENAME);
    var assignmentHistDeserialized = JsonConvert.DeserializeObject<List<AssignmentHistory>>(assignmentHistFile);

    if (null != assignmentHistDeserialized) return assignmentHistDeserialized;

    assignmentHistoryList = new List<AssignmentHistory>();
    return assignmentHistoryList;
}
Label1:

assignmentHistList 属性 的 getter 调用 GetAssignmentHistoryList(),后者递归调用 assignmentHistList 属性 的 getter。

goto Label1;

您可能希望 属性 被称为 AssignmentHistList,大写 A,符合通用代码风格惯例,并具有私有静态字段 assignmentHistList。无论如何,属性 XGetX() 方法存在的双重性很奇怪。

Whosebug 异常通常是由于未正确检测到递归调用条件而发生的。

只有观察下面的代码,我们才能推断出它们是一个视觉循环调用。

assignmentHistList -> GetAssignmentHistoryList,检查条件 null == assignmentHistList 但是为了检查这个条件它需要输入(递归)再次用于 assignmentHistList,然后将再次调用 GetAssignmentHistoryList。你可以看到这是怎么回事。

public static List<AssignmentHistory> assignmentHistList
{
    get { return GetAssignmentHistoryList(); }
}

public static List<AssignmentHistory> GetAssignmentHistoryList()
{
    if (!System.IO.File.Exists(ASSIGNMENT_HISTORY_FILENAME)) return null;
    if (null == assignmentHistList) << Here is another call to the Getter
    // The rest is not important
}

因为您的代码失败的原因已经有了答案,我想 post 对您的代码进行可能的修复:

// this is the backing field for your property
private static List<AssignmentHistory> assignmentHistList;

// it is good practice to name properties starting uppercase
public static List<AssignmentHistory> AssignmentHistList
{
    get 
    {
        // return the content of the backing field if is not null
        return assignmentHistList ??
            // in case the backing field is null,
            // assign it a value from your deserialize method
            // and than return it
            (assignmentHistList = DeserializeAssignmentHistFile());
    }
}

private static List<AssignmentHistory> DeserializeAssignmentHistFile()
{
    // If the file which should contain your data does not exist (yet) return null,
    // the property will retry to set the backing field the next time it is accessed
    if (!System.IO.File.Exists(ASSIGNMENT_HISTORY_FILENAME)) return null;

    var assignmentHistFile 
        = System.IO.File.ReadAllText(ASSIGNMENT_HISTORY_FILENAME);
    var assignmentHistDeserialized 
        = JsonConvert.DeserializeObject<List<AssignmentHistory>>(assignmentHist);

    return assignmentHistDeserialized;
}

然后你可以(大部分)按照你想要的方式调用它:

AssignmentHistory ah = AYttFMConstsAndUtils.AssignmentHistList
    .FirstOrDefault(i => i.WeekOfAssignment == currentWeek && i.TalkType == 1);

请记住,如果应反序列化的文件不存在,这将抛出 ArgumentNullException