Steve
Steve

Reputation: 4645

Call a method with an unknown signature

I'm using C# 4.6.2, though could upgrade to 4.7.2 if it's possible there.

In many places in our code we have a loop with wait statements to check for a specific value when a function is called, wait and retry if it's not what we wanted until a maximum number of retries.

I'd like to abstract this out, but the only implementation I can think of requires you pass in a method with a variable number of arguments of variable types, which after much searching of Google appeared to not be possible about 5 years ago. There has been many improvements to C# since then, so

  1. is it still not possible?
  2. If it is now possible how do I do it?
  3. If it isn't possible can you think of any other way I can achieve my goal?

The sort of thing I'm looking for is:

public bool GenericLoopWait(int maxWaitSeconds, int waitMsPerIteration,??? DoSomething,object expectedResult,...)
    int maxRetries = maxWaitSeconds*1000/waitMsPerIteration;
    SomeType result=null;
    for(int i=0; i<maxRetries; i++){
        result = DoSomething(...);
        if(result==expectedResult) break;
        Thread.Sleep(waitMsPerIteration);
    }
    return result==expectedResult
}

And then both of these would work:

GenericLoopWait(5,500,Browser.Webdriver.FindElements(selector).Any(),true);
GenericLoopWait(5,500,Api.GetSpecificObject(api,objectName),"expectedOutcome");

Upvotes: 2

Views: 292

Answers (2)

Igor
Igor

Reputation: 62238

You could use generics and Func and wrap the actual calls parameters when you call through to the method.

public bool GenericLoopWait<T>(int maxWaitSeconds, int waitMsPerIteration, Func<T> DoSomething, T expectedResult = default(T))
{
    int maxRetries = maxWaitSeconds * 1000 / waitMsPerIteration;
    T result = default(T);
    for (int i = 0; i < maxRetries; i++)
    {
        result = DoSomething();
        if (expectedResult.Equals(result)) break;
        Thread.Sleep(waitMsPerIteration);
    }

    return expectedResult.Equals(result);
}

Calling code:

GenericLoopWait(5, 500, () => Browser.Webdriver.FindElements(selector).Any(), true);
GenericLoopWait(5, 500, () => Api.GetSpecificObject(api,objectName), "expectedOutcome")

Working dotnetfiddle

Upvotes: 7

Bradley Uffner
Bradley Uffner

Reputation: 16991

The general pattern is to create a "Wrapper" method that accepts an Action or a Func as a parameter. Your wrapper can do it's own logic, and Invoke the parameter at the correct time.

As a simple generic example:

public void MethodWrapper(Action action)
{
    Console.WriteLine("begin");
    action.Invoke();
    Console.WriteLine("end");
}

You could then do this:

void Main()
{
    var a = 1;
    var b = 2;

    MethodWrapper(() => DoSomething(a));
    MethodWrapper(() => DoSomethingElse(a,b));
}

public void DoSomething(int a)
{
    Debug.WriteLine($"a={a}");
}

public void DoSomethingElse(int a, int b)
{
    Debug.WriteLine($"a={a}, b={b}");
}

To generate this output:

begin
a=1
end
begin
a=1, b=2
end

For your specific case, your wrapper could take additional parameters specifying things like number of retries, time between calls, or acceptance criteria.

Upvotes: 0

Related Questions