C#, how to avoid recursion elegantly?
I've got an event-function, where the code inside the function may trigger calling the same function again (because a message loop is used). How can I avoid this or better "detect" this most elegantly? When it comes to multiple thread synchronization one could write:
public void Closing(object sender, EventArgs e)
{
lock(m_O开发者_运维知识库bject)
{
<Code, which can trigger Closing again>
}
}
But in my case it is the same thread that may call the same function and thus lock fails to work.
Introduce a private bool m_IsClosing
variable that indicates whether closing is already in progress.
public void Closing(object sender, EventArgs e)
{
lock (m_Object)
{
if (m_IsClosing)
return;
m_IsClosing = true;
try
{
// Code, which can trigger Closing again
}
finally
{
m_IsClosing = false;
}
}
}
I don't think it's particularly elegant, but without knowing more about what you're doing, I can't any better solution.
Edit: adapted example for possible exceptions while closing.
Add a member of your class closingInProgress
initialized to false. Then:
public void Closing(object sender, EventArgs e)
{
if (closingInProgress)
{
return;
}
try
{
closingInProgress = true;
<Code, which can trigger Closing again>
}
finally
{
closingInProgress = false;
}
}
Use a private field to remember whether Closing has already been called. If it has, then abort from the procedure:
private bool ClosingDone;
public void Closing(object sender, EventArgs e)
{
if (!ClosingDone)
{
ClosingDone = true;
// Code, which can trigger Closing again
}
}
A simple solution is to have a flag available in your class which indicates the event is being handled. Using this flag, you can determine whether to run the method again when called recursively.
private bool m_IsClosing;
public void Closing(object sender, EventArgs e)
{
lock(m_Object)
{
// Check for state.
if(m_IsClosing)
return;
m_IsClosing = true;
try
{
// The rest of your code.
}
finally
{
m_IsClosing = false;
}
}
}
The only difficulty will be ensuring you don't read or modify this value elsewhere without entering a critical section locked on m_Object
. Otherwise, it should prevent the event handler doing any work recursively.
It's a bit old, but how about that:
public class Locker : IDisposable
{
bool _isActive;
public bool IsActive
{
get { return _isActive; }
}
private Locker()
{
_isActive = false;
}
public static Locker Create()
{
return new Locker();
}
public void Dispose()
{
_isActive = false;
}
public Locker Activate()
{
_isActive = true;
return this;
}
}
usage:
Locker lo = Locker.Create();
private void Foo()
{
if (lo.IsActive) return;
using(lo.Activate())
{
Console.WriteLine("Foo");
Bar();
}
}
private void Bar()
{
Console.WriteLine("Bar");
Foo();
}
Method Foo() is only called once. using makes sure, that lo is deactivated, even if an error occures.
精彩评论