Search code examples
c#delegatesportable-class-library

Reflection GetMethod issue in Class Library (Portable)


I have a class library in which I have defined a few functions f1, f2 that I would like to put them in a dictionary to later call by name. However, the reflection Type.GetMethod does not seem to be defined in the class library portable even I am using System.Reflection. Can you suggest a work around?

This is my simple piece of code for illustration

   public class Class1
    {
        public delegate int myFunction(object o);
        private Dictionary<string, myFunction> list = new Dictionary<string, myFunction>();
        public Class1()
        {
            string[] n = { "f1", "f2" };
            MethodInfo mInfo;
            foreach (var i in n)
            {
                mInfo = typeof(Class1).GetMethod(i);
            }

            list.Add("f1", f1);
            list.Add("f2", f2);
        }
        public  int f1(object o)
        {
            System.Diagnostics.Debug.WriteLine("f1: parameter is {0} " ,o);
            return 0;
        }
        public int f2(object o)
        {
            System.Diagnostics.Debug.WriteLine("f2: parameter is {0} ", o);
            return 0;
        }
    }

my original intention was putting all my functions in my dictionary list. I can manually add:

                list.Add("f1", f1);
                list.Add("f2", f2);

But when I declare an array of string with the name of the function, use GetMethod to pick up the function, compiler was generating an error "Type does not contain definition for GetMethod...

Please advise. Thanks!

Update: Compiling Error enter image description here

Update with Singleton:

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

namespace ClassLibrary1
{
    public sealed class Class1
    {
        public delegate int myFunction(object o);
        public Dictionary<string, myFunction> list = new Dictionary<string, myFunction>();
        private static readonly Class1 _instance = new Class1();
        static Class1()
        {
        }
        public Class1()
        {
            string[] n = { "f1", "f2" };
            MethodInfo mInfo;
            foreach (var i in n)
            {
                mInfo = typeof(Class1).GetTypeInfo().GetDeclaredMethod(i);
                //NewExpression constrExpr = Expression.New(typeof(Class1).GetTypeInfo().DeclaredConstructors.Single(ci => ci.GetParameters().Length == 0));
                ParameterExpression objExpr = Expression.Parameter(typeof(object));
                Expression<myFunction> expr = Expression.Lambda<myFunction>(Expression.Call(null, mInfo, objExpr), objExpr);
                //Expression<myFunction> expr = Expression.Lambda<myFunction>(Expression.Call(constrExpr, mInfo, objExpr), objExpr);
                list.Add(i, expr.Compile());
            }

            list.Add("f11", f1);
            list.Add("f21", f2);
        }
        public static Class1 Instance
        {
            get
            {
                return _instance;
            }
        }
        public int f1(object o)
        {
            System.Diagnostics.Debug.WriteLine("f1: parameter is {0} ", o);
            return 0;
        }
        public int f2(object o)
        {
            System.Diagnostics.Debug.WriteLine("f2: parameter is {0} ", o);
            return 0;
        }
    }

}

Solution

  • First of all, most PCL profiles are using TypeInfo for type information, see e.g. here and here. TypeInfo also comes with a slightly different set of methods.

    So, instead of writing

    typeof(Class1).GetMethod(i)

    you should thus write:

    typeof(Class1).GetTypeInfo().GetDeclaredMethod(i)
    

    But even if you use the above expression to obtain MethodInfo, you cannot (to my knowledge) immediately convert this object into a myFunction delegate.

    One way of solving this problem would be to use expression trees. You would then need to formulate an Expression<myFunction> object which you would compile into a myFunction delegate.

    I think you can do something like this:

    Expression<myFunction> expr =
        Expression.Lambda<myFunction>(
            Expression.Call(
                constrExpr,
                typeof(Class1).GetTypeInfo().GetDeclaredMethod(i), 
                objExpr),
            objExpr);
    
    list.Add(i, expr.Compile());
    

    where constrExpr is an expression for creating an instance of Class1 using the default constructor

    NewExpression constrExpr =
        Expression.New(
            typeof(Class1).GetTypeInfo().DeclaredConstructors
                .Single(ci => ci.GetParameters().Length == 0));
    

    and objExpr is a representation of the object o parameter

    ParameterExpression objExpr = Expression.Parameter(typeof(object));
    

    With these changes, you should be able to invoke an arbitrary method in your dictionary, like this:

    int i = list["f1"]("hello");
    int j = list["f2"](20.0);