heltonbiker
heltonbiker

Reputation: 27615

How to avoid repetition with multiple overrides of method accepting Func<T, int> as parameters

I would like to have a "generic" - that is, a method with a single signature if possible - instead of this rather copy/paste code below.

My goal is to "wrap" methods from a third party DLL with a Spy Double

    int spy(Func<int> method)
    {
        methodCalls.Add(method.Method.Name);

        return method.Invoke();
    }

    int spy(Func<string, int> method, string arg)
    {
        methodCalls.Add(method.Method.Name);

        return method.Invoke(arg);
    }

    private int spy(Func<int, int> method, int arg)
    {
        methodCalls.Add(method.Method.Name);

        return method.Invoke(arg);
    }

You see the problem: I have almost identical method blocks... Is there a way to avoid this and still accomplish the desired behavior?

Upvotes: 1

Views: 119

Answers (3)

tchelidze
tchelidze

Reputation: 8318

Based on your primary aim

to have a "generic" - that is, a method with a single signature if possible instead of this rather copy/paste code below.

You can try following

object SpyUniversal(Delegate method, object[] args) 
{ 
  methodCalls.Add(method.Method.Name); 
  return method.Method.Invoke(null, args); 
}

Notice that you are losing compile-time type-safety as it was mentioned.

Upvotes: 1

Sergey Berezovskiy
Sergey Berezovskiy

Reputation: 236328

First of all you need generic methods for Func and Action delegates which you want to spy. You need these methods for nice strongly-typed API. You also need a private generic method which accepts a Delegate and a list of arguments. This method will do invoking and logging:

public TResult Spy<TResult> (Func<TResult> method)
{
    return Spy<TResult>((Delegate)method);
}

public TResult Spy<T1, TResult> (Func<T1, TResult> method, T1 arg1)
{
    return Spy<TResult>((Delegate)method, arg1);
}

private TResult Spy<TResult> (Delegate method, params object[] args)
{
    methodCalls.Add(method.Method.Name);
    return (TResult)method.Method.Invoke(method.Target, args);
}

Now you can pass any methods with zero or one argument, execute them and log method name without any duplication.

Upvotes: 2

Jon Skeet
Jon Skeet

Reputation: 1503649

Well you could start off by making them generic:

public TResult Spy<TResult>(Func<TResult> method)
{
    methodCalls.Add(method.Method.Name);
    return method();
}

public TResult Spy<TArg, TResult>(Func<TArg, TResult> method, TArg arg)
{
    methodCalls.Add(method.Method.Name);
    return method(arg);
}

public TResult Spy<TArg1, TArg2, TResult>
    (Func<TArg1, TArg2, TResult> method, TArg1 arg1, TArg2 arg2)
{
    methodCalls.Add(method.Method.Name);
    return method(arg1, arg2);
}

...

That at least means you only need as many overloads as the maximum number of parameters you want to handle. You won't easily be able to do better than that without losing compile-time type-safety.

Upvotes: 2

Related Questions