I have some flags values in an C# windows service which I want to temporarily be able to change but that will automatically revert to some default value after a period of time. The bare bones of the class look something like this
public class Revertable<T>
{
public readonly T DefaultValue;
public T Value { get; protected set; }
public async Task SetValue(T newValue, TimeSpan resetDelay)
{
Value = newValue;
await Task.Delay(resetDelay);
Value = DefaultValue;
}
public void ResetValue()
{
Value = DefaultValue;
}
}
This is all well and good, but if someone calls SetValue
again, I'll now have two interrupts scheduled which could quite possibly step on eachothers toes. One thought was to simply reject any request which tries to call SetValue
if !Value.Equals(DefaultValue)
. That too feels clunky, but sort of works. But one way or another, I need the ability for someone to decide to un-set the value at any time.
I've been looking into the CancellationTokenSource
/CancellationToken
constructs, but I'm fumbling with how to actually set this all up. It seems like I'd need to add a CancellationTokenSource
member to my class (which ResetValue()
or others could make use of), and provide that to the Task.Delay()
call. If someone calls ResetValue()
, the token source initiates a cancel and we move on. So modified, the innards of the class would look something like this:
...
private CancellationTokenSource TokenSource;
public async Task SetValue(T newValue, TimeSpan resetDelay)
{
if (!Value.Equals(DefaultValue))
return;
Value = newValue;
using (CancellationTokenSource src = new CancellationTokenSource())
{
TokenSource = src;
await Task.Delay(resetDelay, src.Token);
}
Value = DefaultValue;
}
public void ResetValue()
{
if (TokenSource != null && TokenSource.Token.CanBeCanceled)
{
TokenSource.Cancel();
TokenSource.Dispose();
}
Value = DefaultValue;
}
I'm somewhat new to async
programming, and I don't want to completely botch doing something which seems like it should be relatively straight forward (or leave a bunch of un-disposed token sources floating around). Does this seem like a valid way to go about this? Or can someone suggest a better way?
You don't need a thread or a task at all. Just use a timestamp and protect your field with a (non-auto) property.
public class Revertable<T>
{
public readonly T DefaultValue;
public readonly TimeSpan ResetDelay;
private T _currentValue;
private DateTimeOffset _resetTime;
public Revertable(T defaultValue, TimeSpan resetDelay)
{
this.DefaultValue = defaultValue;
this.ResetDelay = resetDelay;
_currentValue = defaultValue;
_resetTime = DateTimeOffset.MinValue;
}
public T Value
{
get
{
return (System.DateTimeOffset.Now > _resetTime)
? DefaultValue
: _currentValue;
}
set
{
_currentValue = value;
_resetTime = System.DateTimeOffset.Now.Add(this.ResetDelay);
}
}
}
public static void Main()
{
var r = new Revertable<string>("Default value", new TimeSpan(0,0,1));
Console.WriteLine("Value is now {0}", r.Value);
r.Value = "Changed";
for (int i = 0; i<30; i++)
{
System.Threading.Thread.Sleep(100);
Console.WriteLine("Value is now {0}", r.Value);
}
}
Output:
Value is now Default value
Value is now Changed
Value is now Changed
Value is now Changed
Value is now Changed
Value is now Changed
Value is now Changed
Value is now Changed
Value is now Changed
Value is now Changed
Value is now Default value
Value is now Default value
Value is now Default value
Value is now Default value
Value is now Default value
Full code available on DotNetFiddle.