1

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;
        }
    }

}
user1205746
  • 3,110
  • 11
  • 44
  • 73
  • 1
    If they all have the same signature _and_ you already have access to all the methods at compile time you could just make your dictionary `Dictionary>`. – Mark Feb 17 '16 at 19:48
  • Yes, I'd avoid using reflection for this anyway, but you rexample compiles for me ... – Dan Field Feb 17 '16 at 19:49
  • @DanField: How do you create the library? It has to be Portable class library. My example work with normal console application – user1205746 Feb 17 '16 at 19:51
  • I created a portable class library, copied your class into it, and created a console app to test it. – Dan Field Feb 17 '16 at 19:55
  • @Mark I am still trying to figure out how I am going to add the functions with the names in string[] systematically in the dictionary using your suggestion. Using your suggestion, I will still have to manually add("f1",f1) and so on. Will I not? – user1205746 Feb 17 '16 at 19:57
  • Why are you concerned about that when you're manually constructing the list anyway? – Dan Field Feb 17 '16 at 20:02
  • @DanField: Interesting. I have updated the print screen of the error. I wonder what I did wrong. I am using VS 2015. – user1205746 Feb 17 '16 at 20:05
  • @Mark: I do not plan to manually constructing the list. Just an illustration of what I would like to have as a result. I have hundred of functions, manually adding the function is not efficient to say the least. – user1205746 Feb 17 '16 at 20:09
  • 1
    @user1205746 It seems the PCL has some changes compared to the normal framework: https://blogs.msdn.microsoft.com/dotnet/2012/08/28/evolving-the-reflection-api/ . If I read it correctly then you need to use the `DeclaredMethods` property on the type instead. – Mark Feb 17 '16 at 23:19

1 Answers1

3

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);
Anders Gustafsson
  • 15,837
  • 8
  • 56
  • 114
  • 1
    That is awesome!! Thank you Anders! It works!! This is amazing... I have been struggling with this for a few days before I put it on here.. – user1205746 Feb 18 '16 at 12:31
  • Your suggestion works perfectly! Just would like to have a follow up question: Would it be possible to serialize the values in the list and store them in sql or oracle when expression tree is used instead of the manual add? The reason I ask this is because when I look into the content of the value, the expression tree seems to involve runtime compiler services. – user1205746 Feb 18 '16 at 13:26
  • 1
    I honestly don' know. A quick Googling indicates that expression tree serialization does not come straight out-of-the-box; you would need dedicated tools for it. [Here](http://stackoverflow.com/q/217961/650012) is an old SO question on the subject. – Anders Gustafsson Feb 18 '16 at 13:37
  • Another curious follow up question: would the code above applicable if my class1 is a singleton? – user1205746 Feb 18 '16 at 14:22
  • 1
    You mean if `Class1` is `static`, or? In that case, replace `constrExpr` with `null`. (I think that's all you need to do.) – Anders Gustafsson Feb 18 '16 at 14:26
  • Yes, Class1 being static.. I am new to expression tree and start to like it now.. :) Thanks a bunch!! – user1205746 Feb 18 '16 at 14:28
  • I know I am pushing it but I can not withhold my curiosity :) I have updated the class into a Singleton and there is a run time error. (please see above) A singleton is supposed to be accessed through the only instance declared once. I have even cheated and declared Class1 being public to bypass the compiler complaint. Can not bypass the run time error. – user1205746 Feb 18 '16 at 15:02
  • Never mind, I figure it out. I have to declare the function as static as well to make sure they are created in the stack instead of the heap..I guess. – user1205746 Feb 18 '16 at 15:11
  • Hope it works out. I might take a look tomorrow, but no promises. – Anders Gustafsson Feb 18 '16 at 15:15
  • BTW, if you cannot figure it out yourself, it would be a good idea to post a separate question. That way your remaining problem will be much better exposed. – Anders Gustafsson Feb 18 '16 at 15:25