views:

177

answers:

2

I have a protected method in a base class which accepts a Func<T> and then turns around and executes with some added goodness. Example usage:

public MyResponse DoSomething(MyRequest request)
{
    return base.Execute(() => this.Channel.DoSomething(request));
}

What I'm looking to do is take the func delegate instance and redirect the method call in the expression to another instance besides this.Channel, so something like:

protected TResponse Execute<TResponse>(Func<TResponse> command)
{
    return command.Method.Invoke(this.otherInstanceOfChannel, command.Target);
}

Here the "this.otherInstanceOfChannel" would be an instance of a different concrete class than the "this.channel" passed in the original call but implements the same interface. I just need to figure out what method is being called and execute that on another instance passing in the original arguments from the caller. I started down the path of MethodCallExpressions and the like but my expression-foo is weak...

Edited/rewrote for clarity - hope this version makes more sense.

Thanks, Matt

+1  A: 

I believe that you can provide the instance in the lamda expression like so:

IMyChannel myChannelInstance = MyChannelInstanceFactory.Create();
Execute(() => myChannelInstance.DoSomething(request))

If this can not be done with lamda expressions and I am sure they can you can change this to a delegate and it would work fine. The lamda expression is pointing to a code execution block and as such you can put whatever matches the expression arguments in that code block.

Michael Mann
Sorry - probably wasn't being clear with my question - see my response to Pavel above...
matt
Well you could simplify this by making the base class method virtual and override that in the deriving class. The base class method would invoke on the normal instance and the deriving class could override this behavior standing in it's own instance
Michael Mann
Definitely - if I can't make this work within the Execute method implementation alone, then I'll be looking at ways to change the method signature accommodate my needs and force everyone to change their code - was trying to avoid that if I could.
matt
What about creating an overload of the method instead of overriding it where in the overload you can pass your additional instance that you want to delegate the call to. In the base class it would call this overload with its own default instance type. Does this approach complicate matters. This would allow other developers to provide the instance if they needed to for their situation.
Michael Mann
+1  A: 

Yes you can do this. No time right now to give you the full solution but here is a skeleton of what you would do:

protected TResponse Execute<TResponse>(Expression<Func<TResponse>> command)
{
    // Check that the expression is in the correct format (ie you are calling a method off of a type Channel
    // Get the name of the method call.  Something like:
        var node = expr.Body as MemberExpression;
        if (object.ReferenceEquals(null, node))
            throw new InvalidOperationException("Expression must be of member access");
        var methodName = node.Member.Name;
    // Use reflection to invoke methodName on otherInstanceOfChannel
    // Cast the results to TResponse and return 
}

As you can see the only real trick is the use of Expression<>. The type change is transparent to any client code - they don't have to change at all. Here is some code to get you started with parsing expression trees.

George Mauer
I wound up discovering another way to do it without expression monkeying, but your method did work for the way I was trying to do it - thanks - will remember that for the future.
matt