递归方法调用导致StackOverflow异常

时间:2017-04-17 19:24:09

标签: c# recursion

写完这个问题后,创造了一个MCVE,听起来有点像家庭作业,但实际上它不是...... 不幸的是,我太老了,无法完成作业。

我正在尝试编写一个小应用程序来填充我正在使用'金字塔'结构的数据库。 有一个原始成员,他指的是10个成员。 这些推荐中的每一个都可以有10个推荐。其中每个都有10个转介。等等...

我正在尝试使用最大数量的成员(已提供)填充数据库

如果我将最大成员数设置为100,000 - 这是有效的。 但是,200,000会引发StackOverflow异常。

我很确定这不是因为我没有尽早终止'扇出'。但我无法为我的生活弄清楚在哪里。

注意,下面我的MCVE使用Dapper是为了简单起见,因此简单的INSERT INTO语句

public class MemberPopulator
{
    private readonly SqlConnection connection;

    private const string MemberSql = @"
            INSERT INTO Members (FirstName, LastName, ReferralId, Active, Created) 
            VALUES (@FirstName, @LastName, @ReferralId, @Active, @Created);
            SELECT CAST(SCOPE_IDENTITY() as int)";

    private int TotalMemberCount;
    private const int MaxMemberCount = 200000;

    public MemberPopulator()
    {
        connection = new SqlConnection("Data Source=localhost;Initial Catalog=MyTestDb;Integrated Security=True");
    }

    public void CreateMembers()
    {
        //clear members
        connection.Execute("TRUNCATE TABLE Members");

        //create the 'original' member (top of pyramid)
        var originalMemberId = connection.Query<int>(MemberSql, new Member
        {
            FirstName = "FirstName Goes Here",
            ReferralId = 0
        }).Single();

        //now we have 1 total members
        TotalMemberCount = 1;

        //recursively create members, starting with original member,
        RecursiveCreate(new[] { originalMemberId });
    }

    private void RecursiveCreate(IEnumerable<int> referralMemberIds)
    {
        //don't recurse if we've already got enough members
        if (TotalMemberCount >= MaxMemberCount)
            return;

        foreach (var referralId in referralMemberIds)
        {
            //Create 10 members
            var refs = CreateReferredMembers(referralId, 10);

            RecursiveCreate(refs);
        }
    }

    private IEnumerable<int> CreateReferredMembers(int referralId, int numberOfReferrals)
    {
        var referredMemberIds = new List<int>();

        for (var i = 0; i < numberOfReferrals; i++)
        {
            if (TotalMemberCount >= MaxMemberCount)
                break;

            var member = new Member
            {
                FirstName = "FirstName Goes Here",
                ReferralId = referralId
            };

            var memberId = connection.Query<int>(MemberSql, member).Single();
            referredMemberIds.Add(memberId);
            TotalMemberCount++;
        }

        return referredMemberIds;
    }
}

1 个答案:

答案 0 :(得分:1)

C#中的堆栈对于32位应用程序设置为1MB,对于64位应用程序设置为4MB by default。这适用于大多数应用。如果您需要更多信息,请遵循网络中的指导(例如this one)。

如果您不确切知道递归级别,我建议使用StackQueue数据类型来模拟递归。

public class MemberPopulator
{
    private readonly SqlConnection connection;

    private const string MemberSql = @"
            INSERT INTO Members (FirstName, LastName, ReferralId, Active, Created) 
            VALUES (@FirstName, @LastName, @ReferralId, @Active, @Created);
            SELECT CAST(SCOPE_IDENTITY() as int)";

    private int TotalMemberCount;
    private const int MaxMemberCount = 200000;

    public MemberPopulator()
    {
        connection = new SqlConnection("Data Source=localhost;Initial Catalog=MyTestDb;Integrated Security=True");
    }

    public void CreateMembers()
    {
        //clear members
        connection.Execute("TRUNCATE TABLE Members");

        //create the 'original' member (top of pyramid)
        var originalMemberId = connection.Query<int>(MemberSql, new Member
        {
            FirstName = "FirstName Goes Here",
            ReferralId = 0
        }).Single();

        //now we have 1 total members
        TotalMemberCount = 1;

        //recursively create members, starting with original member,
        NonRecursiveCreate(originalMemberId);
    }

    private void NonRecursiveCreate(int root)
    {
        Queue<int> members = new Queue<int>();

        members.Enqueue(root);

        while (members.Any() && TotalMemberCount < MaxMemberCount)
        {

            var referralId = members.Dequeue();
            //Create 10 members
            var refs = CreateReferredMembers(referralId, 10);
            foreach (int i in refs)
            {
                members.Enqueue(i);
            }
        }
    }

    private IEnumerable<int> CreateReferredMembers(int referralId, int numberOfReferrals)
    {
        var referredMemberIds = new List<int>();

        for (var i = 0; i < numberOfReferrals; i++)
        {
            if (TotalMemberCount >= MaxMemberCount)
                break;

            var member = new Member
            {
                FirstName = "FirstName Goes Here",
                ReferralId = referralId
            };

            var memberId = connection.Query<int>(MemberSql, member).Single();
            referredMemberIds.Add(memberId);
            TotalMemberCount++;
        }

        return referredMemberIds;
    }
}