Red Taz
Red Taz

Reputation: 4179

Get concrete type from method expression

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

Answers (1)

Luaan
Luaan

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

Related Questions