Mike
Mike

Reputation: 2012

Better (generic) declaration of Func delegates

ok, I hope the title of this question makes sense. In my app I have some methods which should be invoked by a special InvokeMethod. At the moment, it works like this:

internal bool RemoteLogin(string password)
{
    return (bool)InvokeMethod(new Func<string, bool>(Server.RemoteLogin), password);            
}

internal string GetSessionId()
{            
    return (string)InvokeMethod(new Func<string>(Server.GetSessionId));                      
}

public object InvokeMethod(Delegate method, params object[] args)
{
    return method.DynamicInvoke(args);
}    

To call InvokeMethod I have to pass a new Func<....>, add the parameter(s) and also cast the return value to the appropriate type. Is there a better (more generic) way to do this, for example using Generics or Reflection?

Any help is highly appreciated.

Upvotes: 4

Views: 575

Answers (5)

Mike
Mike

Reputation: 2012

Additional Info: If a method has no return value (e.g. "void Logout()" ), you can use the Action delegate (method can have thesame name -> InvokeMethod -> because of the different signature/parameter):

public void InvokeMethod(Action method)
{
    method();
}

Upvotes: 1

Bevan
Bevan

Reputation: 44317

You can achieve a certain amount of strong typing - at the expense of repetition by using Func variations:

public R InvokeMethod<T,R>(Func<T,R> method, T argument)
{
    return method(argument);
}   

public R InvokeMethod<T1,T2,R>(Func<T1,T2,R> method, T1 argument1, T2 argument2)
{
    return method(argument1, argument2);
}   

public R InvokeMethod<T1,T2,T3,R>(Func<T1,T2,T3,R> method, T1 argument1, T2 argument2, T3 argument3)
{
    return method(argument1, argument2, argument3);
}   

And so on.

Though this is consistent with your original, there's no actual need to handle parameters at all. Try writing your InvokeMethod this way:

public R InvokeMethod<R>(Func<R> method)
{
    return method();
}

and then call it with this style:

internal bool RemoteLogin(string password)
{
    return InvokeMethod(() => Server.RemoteLogin(password));
}

internal string GetSessionId()
{            
    return InvokeMethod( () => Server.GetSessionId());
}

This way, you leave the parameter handling to the lambda expression and you only need to write InvokeMethod once.

Upvotes: 3

Kyle W
Kyle W

Reputation: 3752

static T Invoke<T>(Func<T> method)
{
  //Log here
  return method();
}

bool RemoteLogin(string password)
{
  return Invoke(() => Server.RemoteLogin(password));
}

Upvotes: 1

Reed Copsey
Reed Copsey

Reputation: 564781

"In my example above, the method InvokeMethod is simplified. In my app it does log, monitor, exception handling etc. of an invocation."

Given this comment, my suggestion would be to rework this. Instead of invoking the delegate as this, you could make the operation take Func<T>, like so:

public T InvokeMethod<T>(Func<T> method)
{
    // Add wrapper as needed
    return method();
}

You could then call it using lambdas when you need to pass parameters:

internal bool RemoteLogin(string password)
{
    return InvokeMethod(() => Server.RemoteLogin(password));            
}

internal string GetSessionId()
{            
    return InvokeMethod(Server.GetSessionId);                      
}

Upvotes: 2

Steve Danner
Steve Danner

Reputation: 22168

I agree with Reed that you really could just call the method directly and eliminate some redundant code, but if you want that strong typing when you are doing these things, it's very easy to rewrite the InvokeMethod call like so.

public static T InvokeMethod<T>(Delegate method, params object[] args)
{
    return (T)method.DynamicInvoke(args);
} 

Then your call above just becomes:

return InvokeMethod(new Func<string, bool>(Server.RemoteLogin), password);  

And the Boolean return type is inferred by the return type on your method.

Upvotes: 1

Related Questions