For the moment I have solve the issue by accepting that casting will be a fact of life for this problem.
The interfaces have been changed to classes because there should really only be one implementation of each and the methods have been simplified by removing the type parameter for the output. Given that the code deals extracts the MethodInfo
from the expression it will still be possible to get the return types without having to define multiple method overloads to be able to have the return type in the method signature.
public sealed class CommandMapper<TCommand>
{
public MethodMapper For<T1, T2, T3>(Expression<Action<TCommand, T1, T2, T3>> methodCall)
{
return CreateMethodMapper(methodCall);
}
}
public sealed class MethodMapper
{
public void To<T1, T2, T3>(Expression<Action<T1, T2, T3>> methodCall)
{
// Do stuff
}
}
With this interface the user calls the methods like this:
var map = CommandMapper<IMyCoolActions>.CreateMap();
map.For<int, int, TimeSpan>((command, first, second, timeout) => command.Add(first, second, timeout))
.To((IPEndpoint ipaddress, int first, int second) => instance.Combine(ipaddress, first, second));
In the CommandMapper
the MethodInfo
is obtained by:
var methodCall = method.Body as MethodCallExpression;
if (methodCall == null)
{
throw new InvalidCommandMethodExpressionException();
}
return methodCall.Method;
In the MethodMapper
besides the MethodInfo
the actual object reference needs to be extracted as well. This is slightly more tricky because the compiler generates a class that holds the actual reference, but fortunately there was a solution on StackOverflow.
var methodCall = method.Body as MethodCallExpression; if (methodCall == null) { throw new InvalidCommandMethodExpressionException(); }
var methodInfo = methodCall.Method;
// if the object on which the method is called is null then it's a static method
object instance = null;
if (methodCall.Object != null)
{
var member = methodCall.Object as MemberExpression;
if (member == null)
{
throw new InvalidCommandMethodExpressionException();
}
// The member expression contains an instance of an anonymous class that defines the member
var constant = member.Expression as ConstantExpression;
if (constant == null)
{
throw new InvalidCommandMethodExpressionException();
}
var anonymousClassInstance = constant.Value;
// The member of the class
var calledClassField = member.Member as FieldInfo;
// Get the field value
instance = calledClassField.GetValue(anonymousClassInstance);
}
return new Tuple<object, MethodInfo>(instance, methodInfo);