Reputation: 4179
If I have an expression in the form of Expression<Func<Delegate>>
is it possible to determine the derived type of the object used to pass in the delegate? Does the expression even contain this information, or is it exclusively representative of the delegate.
A code example should make things clearer.
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace Sandbox {
public interface IContract {
void Method();
}
public class Concrete : IContract {
public void Method() { }
}
public class Test {
private IContract contract = new Concrete();
private Concrete concrete = new Concrete();
public static string GetFullMethodName(Expression<Func<Action>> expression) {
var unaryExpression = (UnaryExpression)expression.Body;
var methodCallExpression = (MethodCallExpression)unaryExpression.Operand;
var methodInfoExpression = (ConstantExpression)methodCallExpression.Arguments.Last();
var methodInfo = (MemberInfo)methodInfoExpression.Value;
var type = methodInfo.DeclaringType;
var name = methodInfo.Name;
return String.Format("{0}.{1}", type, name);
}
public Test() {
var strConcrete = GetFullMethodName(() => concrete.Method); // Sandbox.Concrete.Method
var strContract = GetFullMethodName(() => contract.Method); // Sandbox.IContract.Method
}
}
}
Is it possible to make () => contract.Method
produce
Sandbox.Concrete.Method
Instead of
Sandbox.IContract.Method
Can the expression be altered to support this, or would I be forced to pass an instance of the object as a separate parameter in order to determine it's type?
Upvotes: 2
Views: 394
Reputation: 63772
No, that isn't possible.
The expression tree is built based on compile-time information, and at compile-time, the method being invoked is the virtual IContract.Method
. The fact that at runtime, the Concrete.Method
is actually invoked is irrelevant - that's handled in the virtual dispatch mechanism and the compiler finished its job long before any of this is decided.
The best you can do is try to emulate virtual dispatch yourself. For example, you could try enumerating the interface implementation on the this
instance at runtime, and if you do find a method that matches the interface, you'd return that. However, this is certainly tricky - you need to be able to perfectly emulate the actual virtual dispatch.
Another tricky point is that the way you're trying to get the value is already broken. For example, in my environment, the methodInfoExpression
is not a ConstantExpression
- it's a field on a closure type. Your "parsing" is very fragile.
That said, it's also kind of unnecessary and overcomplicated - you're trying to receive an expression that returns an action and "steal" the value of the action out of the expression. There's already a simple way of doing that - pass that argument directly. No need to hide it in an expression tree:
public static string GetFullMethodName(Action action)
{
return string.Format("{0}.{1}", action.Method.DeclaringType.Name, action.Method.Name);
}
var strConcrete = GetFullMethodName(concrete.Method).Dump(); // Sandbox.Concrete.Method
var strContract = GetFullMethodName(contract.Method).Dump(); // Sandbox.Concrete.Method
You don't need expressions for things that basic reflection already gives you :)
Upvotes: 3