I have recently encountered a situation where my code was receiving objects sent, via a scoping event, from code on another thread. These "scope" objects require my code to change state to reflect the object received. This resulted in my code receiving scoping events at inopportune times (such as in the middle of working on the previous scope change). I needed a way to "de-bounce" the scope changes in order to ignore them while I was updating, and also check when I was finished updating to see what scope changes had occurred while I was processing. Here is a simple solution that I put together to help solve the problem. The first code block shows my usage of the class, and the second is the class itself. I hope you find it interesting (and maybe useful).
// Code Block #1: Usage
private ScopeTracker _scopeTracker = new ScopeTracker( ScopeTracker.TrackingStyle.DiscardAllButLast );
public override void OnScopeChanged(object scope)
{
_scopeTracker.TrackChange( scope );
If( !updatingState )
{
if( _scopeTracker.IsNewScopeChange() )
{
this.UpdateState( _scopeTracker.ConsumeNextScopeChange() );
}
}
}
private void UpdateState ( object o )
{
if( updatingState )
{
return;
}
updatingState = true;
// do work on another thread (possibly)…
updatingState = false;
if( _scopeTracker.IsNewScopeChange() )
{
this.UpdateState( _scopeTracker.ConsumeNextScopeChange() );
}
}
// Code Block #2: Class definition.
public class ScopeTracker
{
public enum TrackingStyle { QueueAll, DiscardAllButLast };
public object LastScopeChange
{
get
{
return _lastScopeChange;
}
}
private TrackingStyle _trackingStyle;
private Queue _trackedScopeChanges = new Queue();
private object _lastScopeChange = null;
public ScopeTracker( TrackingStyle TrackingStyle )
{
_trackingStyle = TrackingStyle;
}
/// <summary>
/// Returns true if the change is tracked, else returns false.
/// </summary>
/// <param name="ScopeObject"></param>
/// <returns></returns>
public bool TrackChange( object ScopeObject )
{
bool bTracked = false;
// If we are only tracking the last scope change, then throw away any previous
// scope changes we are tracking.
if( _trackingStyle == TrackingStyle.DiscardAllButLast )
{
_trackedScopeChanges.Clear();
}
// Only track changes if they are changes.
if( 1 > _trackedScopeChanges.Count )
{
// Since we don't have any changes in the queue check to see if this new
// one is different than our last one.
if( _lastScopeChange != ScopeObject )
{
_trackedScopeChanges.Enqueue( ScopeObject );
bTracked = true;
}
}
else
{
// We have changes in the queue, so see if this new one is the same as the last
// change we put in the queue.
if( ScopeObject != _trackedScopeChanges.Peek() )
{
_trackedScopeChanges.Enqueue( ScopeObject );
bTracked = true;
}
}
return bTracked;
}
public object ConsumeNextScopeChange()
{
if( 1 > _trackedScopeChanges.Count )
{
return null;
}
_lastScopeChange = _trackedScopeChanges.Dequeue();
return _lastScopeChange;
}
public bool IsNewScopeChange()
{
if( 1 > _trackedScopeChanges.Count )
{
return false;
}
else
{
return true;
}
}
public object SyncToValidScope()
{
if( this.IsNewScopeChange() )
{
return this.ConsumeNextScopeChange();
}
else
{
if( null != _lastScopeChange )
{
return _lastScopeChange;
}
else
{
return null;
}
}
}
}