views:

620

answers:

5

I appreciate that similar questions have been asked before, but I am struggling to invoke the Linq Where method in the following code. I am looking to use reflection to dynamically call this method and also dynamically build the delegate (or lambda) used in the Where clause. This is a short code sample that, once working, will help to form part of an interpreted DSL that I am building. Cheers.

    public static void CallWhereMethod()
    {
        List<MyObject> myObjects = new List<MyObject>(){new MyObject{Name="Jon Simpson"}};
        System.Delegate NameEquals = BuildEqFuncFor<MyObject>("Name", "Jon Simpson");
        object[] atts = new object[1] ;
        atts[0] = NameEquals;

        var ret = typeof(List<MyObject>).InvokeMember("Where", BindingFlags.InvokeMethod, null, InstanceList,atts);
    }

    public static Func<T, bool> BuildEqFuncFor<T>(string prop, object val)
    {
        return t => t.GetType().InvokeMember(prop,BindingFlags.GetProperty,
                                             null,t,null) == val;
    }
A: 

Extention methods are a c# compiler trick, and they don't exist in the type concerned. They (these particular ones) exist in static classes within the System.Linq name spaces. I'd suggest reflecting this in reflector and then invoking reflection on these types.

Preet Sangha
They're a .NET trick, not a C# one. VB.NET can make and use them, too, though the syntax is different, and involves attributes.
Steven Sudit
@ Didn't know that - cheers.
Preet Sangha
They're still a compiler trick, it's just the VB.NET compiler also uses the same trick.
ICR
That's true, ICR. The end result is that, at the CIL level, it's still a call to a static method, but it LOOKS like an instance method in the source code. Pure syntactic sugar, but still sweet.
Steven Sudit
+7  A: 

Extension methods are really just static methods underwater. An extension method call like foo.Frob(arguments) is really just SomeClass.Frob(foo, arguments). In the case of the Where method, you're looking for System.Linq.Enumerable.Where. So get the typeof Enumerable and invoke Where on that.

Joren
I get a MissingMethodException usingvar ret = typeof(System.Linq.Enumerable).InvokeMember("Where", BindingFlags.InvokeMethod, null, InstanceList, atts);Any ideas? Thanks.
Jon Simpson
The problem is that the method is generic. I found a pretty good blog post on invoking generic methods (http://blogs.microsoft.co.il/blogs/gilf/archive/2008/10/10/invoking-generic-methods-with-reflection.aspx), but there is one additional problem: There are two overloads of Enumerable.Where, differing only in the type on one of the parameters (Func<T, bool> versus Func<T, int, bool>) and I haven't found out yet how to neatly select the one you want.
Joren
+1  A: 

Your code sample is a little confusing... unless MyObject is an enumerable.

Using reflection you'll have to invoke Where on System.Linq.Enumerable, passing in the enumerable you want to preform Where on.

Will
Well spotted, there was an error in my question (now updated), I have tried var ret = typeof(System.Linq.Enumerable).InvokeMember("Where", BindingFlags.InvokeMethod, null, InstanceList, atts);without success. Any ideas? Thanks.
Jon Simpson
The first argument has to be the enumerable to be acted upon. The second must be the func that returns true or false. Also, invoking a static function is a little tricky; been awhile since I've done it--I'd suggest invoking a simple static to ensure you have the command correct.
Will
+7  A: 

As others said, extensions methods are compiler magic, you can alway use VS right click, go to definition to find the real type that implements the static method.

From there it gets fairly hairy. Where is overloaded, so you need to find the actual definition that matches the signature you want. GetMethod has some limitations with generic types so you have to find the actual one using a search.

Once you find the method you must make the methodinfo specific using the MakeGenericMethod call.

Here is a full working sample:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;

namespace ConsoleApplication9 {
    class Program {

        class MyObject {
            public string Name { get; set; }
        } 

        public static void CallWhereMethod() {
            List<MyObject> myObjects = new List<MyObject>() { 
                new MyObject { Name = "Jon Simpson" },
                new MyObject { Name = "Jeff Atwood" }
            };


            Func<MyObject, bool> NameEquals = BuildEqFuncFor<MyObject>("Name", "Jon Simpson");


            // The Where method lives on the Enumerable type in System.Linq
            var whereMethods = typeof(System.Linq.Enumerable)
                .GetMethods(BindingFlags.Static | BindingFlags.Public)
                .Where(mi => mi.Name == "Where"); 

            Console.WriteLine(whereMethods.Count());
            // 2 (There are 2 methods that are called Where)

            MethodInfo whereMethod = null;
            foreach (var methodInfo in whereMethods) {
                var paramType = methodInfo.GetParameters()[1].ParameterType;
                if (paramType.GetGenericArguments().Count() == 2) {
                    // we are looking for  Func<TSource, bool>, the other has 3
                    whereMethod = methodInfo;
                }
            }

            // we need to specialize it 
            whereMethod = whereMethod.MakeGenericMethod(typeof(MyObject));

            var ret = whereMethod.Invoke(myObjects, new object[] { myObjects, NameEquals }) as IEnumerable<MyObject>;

            foreach (var item in ret) {
                Console.WriteLine(item.Name);
            }
            // outputs "Jon Simpson"

        }

        public static Func<T, bool> BuildEqFuncFor<T>(string prop, object val) {
            return t => t.GetType().InvokeMember(prop, BindingFlags.GetProperty,
                                                 null, t, null) == val;
        }

        static void Main(string[] args) {
            CallWhereMethod();
            Console.ReadKey();

        }
    }
}
Sam Saffron
Thanks for putting the effort into this. Much appreciated.
Jon Simpson
I suppose then that there is no direct way to have the framework select the appropriate method? That was what I was looking for, but wasn't able to find anything.+1 For working it out.
Joren
I love the irony of using the where method to find the where method :-)
Preet Sangha
You saved my week :)
Commander Keen
A: 

thank for your code...i tranformed in vb.net...works fine::::

Klaus