简体   繁体   中英

Best way to define a method which can accept an Action or a Func delegate

I would like to introduce a method/function which can receive an Action or a Func<T> , and depending on what it got, it should return void or T .

Now I have to write this method in two, almost same versions like this.

public void WrapAction(Action action) {
  // do something...
  action();
  // do something more...
}

public T WrapFunc(Func<T> func) {
  // do something...
  var result = func();
  // do something more...
  return result;
}

Is there any technique to avoid this repetition?

In contrast, for example in Javascript I have the flexibility to accept any kind of a function (may be void ) and just return it's result, and if it was void , then it will return undefined . But in C# one cannot write something like Func<void> .

One possible solution is that you can extract the repeating bits out to separate methods in the same class so, something like this...

public void WrapAction(Action action) {
  DoInitialBit();
  action();
  DoFinalBit();
}

public T WrapFunc(Func<T> func) {
  DoInitialBit();
  var result = func();
  DoFinalBit();
  return result;
}

private void DoInitialBit()
{
    // Do the thing before you call the Action or Func
}

private void DoFinalBit()
{
    // Do the thing after you call the Action or Func
}

Obviously, you may have to take inputs or return outputs from these additional methods as required but that's the general gist of it.

What about to make an Func<bool> out of an action via an extension method and reduce the wrapper to handle Func<T> only.

public static class ActionExtensions
{
    public static Func<bool> ToFunc(this Action action)
    {
        return () =>
        {
            action();
            return true;
        };
    }
}

Then WrapAction could simply call WrapFunc .

public void WrapAction(Action action)
{
    WrapFunc(action.ToFunc());
}

Or remove WrapAction at all and use WrapFunc directly.

WrapFunc(action.ToFunc());    

Some really ugly code to have code DRY:

static void Main(string[] args)
{
    var p = new Program();
    p.WrapAction(() => Console.WriteLine(123));
    Console.WriteLine(p.WrapFunc<string>(() => "321"));
    Console.ReadKey();
}

public void WrapAction(Action action) => WrapActionInner<object>(action);

public T WrapFunc<T>(Func<T> func) => WrapActionInner<T>(func);

private T WrapActionInner<T>(object action)
{
    if (action is Action)
    {
        ((Action)action)();
        return default(T);
    }
    return ((Func<T>)action)();
}

The idea is to wrap functionality into type unsafe private method.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM