Francesco's blog

 Monday, November 07, 2005

Here's a non-orthodox but quite effective technique I sometimes use to detect and avoid recursive calls to a method. You typically detect recursive calls by defining a boolean class-level field and testing it on entry to a method. This technique is often used in event handlers, for example in TextChanged handlers that modify the Text property of a control and that would therefore trigger an endless recursion:

Dim insideTextChanged As Boolean

Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
   ' Exit if this is a recursive call.
  
If insideTextChanged Then Exit Sub
  
' Forbid recursive calls from now on.
   insideTextChanged = True
  
' ...
  
TextBox1.Text = TextBox1.Text & " "
   ' Permit recursive calls.
  
insideTextChanged = False
End Sub

This approach works well, but it requires a lot of code and forces you to define a distinct boolean field for each event handler. If you have many handlers, it quickly becomes a nuisance. In addition, if there is any chance that the method throws an exception, you must wrap all the code in a try block,so that you can reset the insideTextChanged to false in the finally section. Wouldn't it great if you could use a method that allows you to test if you are inside a recursive call? I am thinking of something like this:

Private Sub TextBox1_TextChanged(ByVal sender As Object, ByVal e As System.EventArgs) Handles TextBox1.TextChanged
   ' Exit if this is a recursive call.
  
If IsRecursive() Then Exit Sub
   ' ...
  
TextBox1.Text = TextBox1.Text & " "
End Sub

Here's how you can implement the IsRecursive method:

<System.Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)> _
Public Shared Function IsRecursive() As Boolean
  
Dim st As New StackTrace
   ' Check whether any method in the call stack is the same as the immediate caller.
  
For n As Integer = 2 To st.FrameCount - 1
      If st.GetFrame(1).GetMethod() Is st.GetFrame(n).GetMethod() Then Return True
  
Next
  
Return False
End Function

Here's the C# version:

[System.Runtime.CompilerServices.MethodImpl(Runtime.CompilerServices.MethodImplOptions.NoInlining)]
public static bool IsRecursive() 
{
  
StackTrace st = new StackTrace();
   // Check whether any method in the call stack is the same as the immediate caller.
  
for ( int n= 2; n < st.FrameCount; n++ )
   {
      if ( st.GetFrame(1).GetMethod() == st.GetFrame(n).GetMethod()
        
return true;
   
}
  
return false;
}

The IsRecursive method compares the immediate caller - that is, st.GetFrame(1).GetMethod() - with all the other methods on the call stack and returns True if it finds a match. It is essential that the IsRecursive method is decorated with the MethodImpl attribute, to ensure that the JIT compiler inlines it in its caller's body. In .NET 1.1 this should never happen, because the JIT compiler never inlines methods that contain loops, but I haven't checked under .NET 2.0 and obviously I can't make promises about future versions, therefore this attribute is your best defence.

 
Get RSS/Atom Feed
RSS 2.0 | Atom 1.0
Search in the blog
Archive
<May 2008>
SunMonTueWedThuFriSat
27282930123
45678910
11121314151617
18192021222324
25262728293031
1234567
Categories

Powered by: newtelligence dasBlog 1.8.5223.1