Max Schmeling
Max Schmeling

Reputation: 12509

How do I take a lambda expression as a parameter that returns a method?

I want to be able to do this:

var test = SomeMethod(s => s.SomeMethod);

I can make it work with properties by making the method signature look like this:

SomeMethod<TProperty>(Expression<Func<T, TProperty>> expression)

How can I make it work with methods? I know this is simple, I'm just missing something small.

Upvotes: 2

Views: 653

Answers (5)

Dan Bryant
Dan Bryant

Reputation: 27505

Moq approaches this by requiring the method parameters to be stubbed out with special 'marker' objects like:

test.Setup(m => m.SomeMethod(It.IsAny<int>()));

Incidentally, this allows Moq to resolve method overloads (a single method name is ambiguous if you don't have any notion of the required parameters.) They are going a bit further and using it to actually match parameters based on criteria, for the purpose of mocking objects for unit testing. The source code is available and might give you some ideas. They do all sorts of fun trickery with expression trees.


Here is an example based on the Moq source (hacked out a bit, but shows how the method can be extracted from the expression):

    internal static void ExtractMethod<T>(Expression<Action<T>> expression)
        where T : class
    {
            var methodCall = expression.ToMethodCall();
            var method = methodCall.Method;
            var args = methodCall.Arguments.ToArray();
    }

Straight from Moq source:

    /// <summary>
    /// Casts the body of the lambda expression to a <see cref="MethodCallExpression"/>.
    /// </summary>
    /// <exception cref="ArgumentException">If the body is not a method call.</exception>
    public static MethodCallExpression ToMethodCall(this LambdaExpression expression)
    {
        Guard.NotNull(() => expression, expression);

        var methodCall = expression.Body as MethodCallExpression;
        if (methodCall == null)
        {
            throw new ArgumentException(string.Format(
                CultureInfo.CurrentCulture,
                Resources.SetupNotMethod,
                expression.ToStringFixed()));
        }

        return methodCall;
    }

This will work with your code, but you'd have to pass 'dummy' parameters to allow the compiler to create the expression. So if you had:

public void SomeMethod(int value, string text) {}

Then you'd pass it as:

ExtractMethod(s => s.SomeMethod(0, null));

Upvotes: 1

MartinStettner
MartinStettner

Reputation: 29174

I think, there's no way of doing this without specifying the method signature: Just think of overloaded methods:

void SomeMethod() { }
int SomeMethod(int a, int b) { return 0; }

// ...

var test = TestMethod(s => s.SomeMethod);

How would the compiler possibly know, which of the methods you want to test?

Upvotes: 0

Dan Tao
Dan Tao

Reputation: 128317

Is something like this what you're looking for?

DoSomethingWithAction<T>(Func<T, Action> actionRetriever) { }
DoSomethingWithFunction<T, TResult>(Func<T, Func<TResult>> functionRetriever) { }

You would call these like:

DoSomethingWithAction<ObjectWithMethod>(obj => obj.Method);
DoSomethingWithFunction<ObjectWithProperty>(obj => obj.Property);

Upvotes: 1

Stan R.
Stan R.

Reputation: 16065

class Program
    {
        public class Test
        {
            public bool SomeMethod(string test)
            {
                return true;
            }
        }

        static void Main(string[] args)
        {

            Test testObj = new Test();


            Func<string, bool> rule1 = AddRule(testObj, x => x.SomeMethod);

            bool rsult = rule1("ttt");

        }

        static Func<string, bool> AddRule<T>( T obj, Func<T,Func<string, bool>> func)
        {
            return func(obj);
        }


}

Upvotes: 0

David Lynch
David Lynch

Reputation: 1716

Something like this?

    public SimpleCommand( Predicate<object> canExecuteDelegate, Action<object> executeDelegate )
    {
        CanExecuteDelegate = canExecuteDelegate;
        ExecuteDelegate = executeDelegate;
    }

You will need to specify the function signature using Predicate or Action.

Upvotes: 0

Related Questions