4

I've to call a function recursively. But after a moment it throws StackOverFlowException. When I used Invoke(new Action(Start)) method, it throws same exception but not in a long moment, this is shorter than the previous one.

How can I overcome this problem?

Example Code:

private void Start()
        {
            // run select query
            mysql(selectQueryString.ToString());
            msdr = mysql();
// is finished
            if (!msdr.HasRows)
            {
                this.Finish();
                return;
            }
            //  get mysql fields
            string[] mysqlFields = Common.GetFields(ref msdr);
            while (msdr.Read())
            {
                // set lastSelectID
                lastSelectID = Convert.ToInt32(msdr[idFieldName].ToString());
                // fill mssql stored procedure parameters
                for (int i = 0; i < matchTable.Count; i++)
                {
                    string valueToAdd = Common.ConvertToEqualivantString(matchTable[i].Type, matchTable[i].Value, ref msdr, ref id, matchTable[i].Parameters);
                    sql.Ekle(matchTable[i].Key, valueToAdd);
                }
                // execute adding operation
                lastInsertID = (int)sql(false);
                // update status bar
                this.UpdateStatusBar();
// update menues
                this.UpdateMenues();
                // increment id for "{id}" statement
                id++;
            }
//  close data reader
            msdr.Close();
            msdr.Dispose();
            mysql.DisposeCommand();
// increment select limit
            selectQueryString.LimitFirst += selectQueryString.LimitLast;
            // call itself until finish
            this.Start();
        }
Mesut
  • 1,845
  • 4
  • 24
  • 32
  • How many recursions are you expecting? Of course you'll eventually blow the stack.. You have no condition that stops the recursions.. it continues going forever as far as I can see.. – Simon Whitehead Sep 02 '13 at 22:21
  • @SimonWhitehead The terminating condition is there at the top of the method, with an early return. But the condition doesn't look like a good one to me. Edit: and I was wrong about that, `mysql()` returns something different thing every time. –  Sep 02 '13 at 22:23
  • // increment select limit selectQueryString.LimitFirst += selectQueryString.LimitLast; – Mesut Sep 02 '13 at 22:25
  • 1
    Dude, for Christ's sake, use an ORM. Stop trying to reinvent the wheel. Remove all this horrible code from your code behind. Learn and use a proper architecture and design pattern such as MVC or MVVM, instead of mashing all together UI, Business Logic and Data Access. – Federico Berasategui Sep 02 '13 at 22:25
  • @hvd Yes but.. as far as I can see.. that will always return true.. unless the data in the database is changing, `msdr.HasRows` will always be true.. – Simon Whitehead Sep 02 '13 at 22:26
  • This is a backup program. Not any web site. This is the best chose for me. After getting not value, it ends. – Mesut Sep 02 '13 at 22:27
  • @SimonWhitehead Yeah, that tricked me too, and I had edited my previous comment very shortly before your new comment. There is relevant code missing from the OP's question that would have made it obvious. –  Sep 02 '13 at 22:28
  • I always increment "LIMIT" statement for mysql query. AFter results end, I finish. – Mesut Sep 02 '13 at 22:32
  • 6
    Why are you recursing at all?!? The correct way to do this is to *loop* until there are no more rows, not recurse until done and hope that you have enough stack. The way to fix this is to change your recursion into an outer loop. – RBarryYoung Sep 02 '13 at 22:33
  • Yes, changing code into while loop was the best chois. – Mesut Sep 03 '13 at 00:09

2 Answers2

24

When the last statement in a function is the call to the function itself, you have tail-recursion. While there are languages that optimize tail-recursion to avoid a stack overflow exception, C# is not one of them.

Recursion is not a good pattern for data that can be of an arbitrary length. Simply replace recursion by a while loop:

private void Start()
{
    while(true) {
        // run select query
        mysql(selectQueryString.ToString());
        msdr = mysql();
        // is finished
        if (!msdr.HasRows)
        {
            this.Finish();
            break;
        }
        //  rest of your code..
    }
}
FarmerBob
  • 1,314
  • 8
  • 11
  • 7
    +1 for mentioning that the C# compiler doesn't do tail recursion, and its relevance to this question. – Mike Mertsock Sep 03 '13 at 00:59
  • Better say that C# does not guarantee that tail call will work without stack overflow. At least on x64, tail call does work. http://stackoverflow.com/questions/15864670/generate-tail-call-opcode – tia Sep 03 '13 at 01:09
0

You must always have the breaking condition in the recursion. If you need the best sample to implement the recursion in effective way then you can follow this approach recursion example

negaboys
  • 42
  • 4