【问题标题】:Why is my getter being called until there is a StackOverflow? [closed]为什么在出现 StackOverflow 之前调用我的 getter? [关闭]
【发布时间】:2016-02-21 15:31:29
【问题描述】:

我在我的 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 合并运算符建议,但为了防止出现空文件的可能性,我还必须修改我的 Deserialize 方法,以便它现在是:

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;
}

【问题讨论】:

  • 你看到assignmentHistListGetAssignmentHistoryList之间的循环引用了吗?使用调试器单步执行它,您会发现您不断在两者之间切换。
  • 在您的GetAssignmentHistoryList() 中,您返回您的assignmentHistList 属性,该属性本身返回的是GetAssignmentHistoryList() 的结果。
  • GetAssignmentHistoryList 读取属性“assignmentHistList”,其获取函数调用 GetAssignmentHistoryList。如果您使用 F11 单步执行代码,则应该很明显。

标签: c# linq json.net getter accessor


【解决方案1】:

因为您的代码失败的原因已经有了答案,所以我想对您的代码发布一个可能的修复:

// 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

【讨论】:

  • 谢谢,abto;我想奖励你一笔赏金,但除非重新打开问题,否则这似乎是不可能的......
【解决方案2】:
Label1:

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

goto Label1;

您可能希望将属性命名为 AssignmentHistList,大写 A,符合通用代码样式实践,并具有私有静态字段 assignmentHistList。无论如何,属性XGetX() 方法存在的对偶性很奇怪。

【讨论】:

  • goto 的罕见用途之一,可能。
  • @quetzalcoatl 我真的很喜欢在过去 20 年左右的时间里第一次将它用于实际目的:D
  • B.Clay Shannon 的注释:很可能不是小写的 assignmentHistList getter-only 属性,您希望为大写的 GetAssignement... 提供一个简单的支持字段——我的意思是:private static List&lt;AssignmentHistory&gt; assignmentHistList; .另外,我注意到您正在尝试在 GetAssignment.. 中实现延迟初始化 - 为此您需要将 return DeserializeAssignmentHistFile(); 更改为 assignmentHistList = DeserializeAssignmentHistFile(); return assignmentHistList;
【解决方案3】:

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

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

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
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-09-06
    • 1970-01-01
    • 1970-01-01
    • 2022-01-09
    • 2013-12-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多