如何避免静态构造函数中的StackOverflowException与大型列表的“Lazy”初始化

时间:2016-10-18 05:44:30

标签: c# visual-studio stack-overflow lazy-initialization

以下代码将System.StackOverflowException投放在 VS2015 Update 3 (Resharper 2016,Windows 10 x64,.NET 4.5)中。 IMO,这是由于City - >的静态构造函数中的循环初始化。 Authority - > Country - > City。奇怪的是,它正在使用VS2015 Update 2.

这是我在Visual Studio中的例外:

System.StackOverflowException {"Exception of type 'System.StackOverflowException' was thrown."}
Data : {System.Collections.ListDictionaryInternal}
Count: 0
IsFixedSize: false
IsReadOnly: false
IsSynchronized: false
Keys : {System.Collections.ListDictionaryInternal.NodeKeyValueCollection}
   []
SyncRoot : {object}
Values: {System.Collections.ListDictionaryInternal.NodeKeyValueCollection}
   []
HelpLink: null
HResult: -2147023895
InnerException: null
Message: Exception of type 'System.StackOverflowException' was thrown.
Source: null
StackTrace: null
TargetSite: null

我想知道为什么它适用于Update 2但不适用于Update 3,更重要的是,我该怎么做才能修复它?这个测试代码:

public class Program 
{
    public static void Main(string []args)
    {
        var p1 = PeopleTestData.GetByID(1);
        Console.WriteLine(p1.Name);
    }
}

以下是使用Lazy和静态构造函数的初始化:

public static class PeopleTestData 
{
    public static readonly Lazy<List<Person>> People;
    static PeopleTestData() 
    {
        People = new Lazy<List<Person>>(() => new List<Person>
        {
            new Person {Name = "Person1", City = CityTestData.GetByID(1), ID = 1},
            new Person {Name = "Person2", City = CityTestData.GetByID(2), ID = 2},
            //~6000 records
        });
    }
    public static Person GetByID(int personID)
    {
        return People.Value.Single(p => p.ID == personID);
    }
}

public static class CityTestData
{
    public static readonly Lazy<List<City>> Cities;
    static CityTestData()
    {
        Cities = new Lazy<List<City>>(() => new List<City>
        {
            new City {Name = "City1", Authority = AuthorityTestData.GetByID(1), Country = CountryTestData.GetByID(1), ID = 1},
            new City {Name = "City2", Authority = AuthorityTestData.GetByID(2), Country = CountryTestData.GetByID(2), ID = 2},
            //~5000 records
        });
    }
    public static City GetByID(int cityID)
    {
        return Cities.Value.Single(p => p.ID == cityID);
    }
}

public static class CountryTestData
{
    public static readonly Lazy<List<Country>> Countries;
    static CountryTestData()
    {
        Countries = new Lazy<List<Country>>(() => new List<Country>
        {
            new Country {Name = "Country1", Cities = CityTestData.Cities.Value.Where(c=>c.Country.ID == 1).ToList(), ID = 1},
            new Country {Name = "Country2", Cities = CityTestData.Cities.Value.Where(c=>c.Country.ID == 2).ToList(), ID = 2},
            //~200 records
        });
    }
    public static Country GetByID(int countryID)
    {
        return Countries.Value.Single(p => p.ID == countryID);
    }
}

public static class AuthorityTestData
{
    public static readonly Lazy<List<Authority>> Authorities;
    static AuthorityTestData()
    {
        Authorities = new Lazy<List<Authority>>(() => new List<Authority>
        {
            new Authority {Name = "Authority1", Country = CountryTestData.GetByID(1), ID = 1},
            new Authority {Name = "Authority2", Country = CountryTestData.GetByID(2), ID = 2},
            //~3000 records
        });
    }
    public static Authority GetByID(int authorityID)
    {
        return Authorities.Value.Single(p => p.ID == authorityID);
    }
}

域名实体:

public class Person
{
    public int ID;
    public string Name;
    public City City;
}
public class City
{
    public int ID;
    public string Name;
    public Country Country;
    public Authority Authority;
}
public class Authority
{
    public int ID;
    public string Name;
    public Country Country;
}
public class Country
{
    public int ID;
    public string Name;
    public List<City> Cities;
}

2 个答案:

答案 0 :(得分:0)

你不应该确定任何循环依赖。

你确定它在Update2中每次都吗?我怀疑。

由于您使用的是Lazy初始化,因此可能会执行一个没有循环的执行顺序。尽管存在循环依赖性,但你很幸运能够执行它。

答案 1 :(得分:0)

静态构造函数的循环依赖性会导致死锁,而不会导致堆栈溢出。在您的情况下,静态构造函数已完成,但延迟初始化没有。

Lazy<T>实例完全初始化之前,初始化该实例的重入尝试将从头开始。 是堆栈溢出的来源。

如果没有可靠地再现问题的良好Minimal, Complete, and Verifiable code example(但仍能准确反映您的情况),则无法提供具体的解决方案。但是,当然,你需要让你的类型的初始化依赖于彼此的初始化。 (和其他的回答者一样,我怀疑这可以在任何版本的Visual Studio中运行......它甚至不依赖于VS版本。)

您需要找到至少一种相关类型的方法来完成其初始化并且可用于其他类型,而不依赖于其他类型。