1

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?

Community
  • 1
  • 1
KDecker
  • 6,928
  • 8
  • 40
  • 81
  • Use solutions like these http://stackoverflow.com/questions/1749966/c-sharp-how-to-determine-whether-a-type-is-a-number to determine whether T is a numeric type. – adjan Apr 25 '16 at 16:32

1 Answers1

1

There's no way to do this in C#, sadly.

However, Jon Skeet and Marc Gravell have put together a generic operator framework that allows to perform operations such as addition, subtraction, multiplication and division on values.

Sean
  • 60,939
  • 11
  • 97
  • 136