2

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.

Leniel Maccaferri
  • 100,159
  • 46
  • 371
  • 480
Max Schmeling
  • 12,363
  • 14
  • 66
  • 109

5 Answers5

1

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);
Dan Tao
  • 125,917
  • 54
  • 300
  • 447
1

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));
Dan Bryant
  • 27,329
  • 4
  • 56
  • 102
0

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.

David Lynch
  • 1,716
  • 15
  • 15
  • Definitely not the first parameter, because Predicate returns true or false... I don't think the second one works unless I"m doing it wrong... In my SomeMethod method I need to be able to access information about the method itself that was passed in. – Max Schmeling Jul 22 '10 at 18:35
  • I missed that part then. So you need an expression to get the name of the function (or some other thing)? – David Lynch Jul 22 '10 at 18:39
  • Yeah, basically I need the name... and information about the parameters it takes if possible (not necessary though atm) – Max Schmeling Jul 22 '10 at 18:40
0
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);
        }


}
Stan R.
  • 15,757
  • 4
  • 50
  • 58
  • This way of doing it requires me to know the method signature, correct? That isn't a possibility. – Max Schmeling Jul 22 '10 at 18:46
  • yeah, there is no way without knowing the method signature as they could be an infinite possibilities. – Stan R. Jul 22 '10 at 18:47
  • There has to be a way... even if it isn't quite as clean... maybe I can pass a delegate type as a generic parameter... hmm – Max Schmeling Jul 22 '10 at 18:51
  • I guess that wouldn't be any different from knowing the types – Max Schmeling Jul 22 '10 at 19:27
  • I find it hard to believe there isn't a way around this... I don't want to resort to magic strings... SecurityFor("Delete")... I guess I could just use T4 to generate constants or something like that... *sigh* – Max Schmeling Jul 23 '10 at 23:11
0

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?

MartinStettner
  • 28,719
  • 15
  • 79
  • 106