I am creating a static class that is used to asynchronously wait for some T targetValue
to be "approximately" some T currentValue
. I have already implemented a function to check for exact equality as shown below, but I am having a difficult time with an "approximate value"
public delegate T GetValueMethod<T>() where T: struct;
public static Task<Boolean> WaitForExactValueAsync<T>(GetValueMethod<T> getValue, T targetValue, CancellationToken cancelWaitToken, int msTimeout = 10000) where T: struct
{
return Task.Run<Boolean>(async () =>
{
// Create a CancellationToken that times out, then link it with the argument token that requests a the wait to be cancelled
var timeoutTokenSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(msTimeout));
var timeoutAndCancelTokenSource = CancellationTokenSource.CreateLinkedTokenSource(timeoutTokenSource.Token, cancelWaitToken);
// Continually check to see if we reached the target value or have timed out
T currentValue = getValue();
while (!targetValue.Equals(currentValue) && !timeoutAndCancelTokenSource.IsCancellationRequested)
{
try { await Task.Delay(500, timeoutAndCancelTokenSource.Token); }catch (Exception ex) { }
currentValue = getValue();
}
// If we got here and the token is not cancelled that means we have reached the target value
return !timeoutAndCancelTokenSource.IsCancellationRequested;
});
}
The methods I am creating are meant to only work with primitive types such as int
, double
, Boolean
, etc. I do this by implementing this SO anwser which describes a way to limit generic types to primitives by forcing the T
to be a struct
.
The function above seems to work nicely for my situation because (I think) we can always equate struct
with Equals
to determine equality. My issue when creating the approximately equal version is when the type of T
is something like Boolean
.
I plan to allow the user to pass in a "closeness" percentage to the function to specify when the two values are "approximately equal" (If the current is within x percentage of the target return true). If T
is a numeric value I can do this with maths easily, but when its non-numeric it makes no sense to use this method but to simply use Equals
.
Is there a way to tell from T
(where T : struct
) if I can perform the maths I need to on it (To implement the IsValueClose
method below)?
private static Boolean ValueIsClose<T>(T target, T value, double percentAsDecimal)
where T: struct
{
// if(value.CanDoTheMaths)
{
return Math.Abs((value / target) - target) <= percentAsDecimal;
}
// else
{
return target.Equals(value);
}
}
Furthermore once this happens how can I refer to target
and value
as a type that can perform these maths?
From asking somewhat relate questions before I do not think this is quite possible or good practice for generic methods. So from what I can currently tell, my only good solution is to make an "approximate" method for each type I plan to use (the best practice), or to check the Type
of T
in IsValueClose
to see if it is something that I know I can do maths on and go from there (Bad practice for generics).
Though possibly there is a simpler more elegant solution that someone could suggest?