Calling a method with parameters by name (or: building a method call like a sql qry)

dipique

Member
Joined
Oct 22, 2014
Messages
7
Programming Experience
1-3
.Net Version 4.5.1


Very Brief Background
I have a console application where the user enters a function and parameters. Rather than adding supported functions to a dictionary of some kind, I'd like to just have it call the method by name, using the method signature to validate the parameters. I can gather all the information I need in theory, but can't figure out how to CALL the expression.

I think I know why--it's because I can't figure out how to coerce my parameters into a form the Expression.Call line will accept. I tried using the ParameterExpression class, but it doesn't seem to contain the values, only the types and names.

What I Want to Do

C#:
        /// <summary>
        /// Call a method in this class instance by name and parameters
        /// </summary>
        /// <param name="methodName"></param>
        /// <param name="args"></param>
        /// <returns></returns>
        public void Execute(string methodName, object[] args)
        {
            //get method we will be calling
            MethodInfo method = GetMethodByName(methodName);


            //validate & retrieve parameters based on the method signature       
            object[] parameters;
            if (!GetAndValidateParams(
                        method.GetParameters().Select(pd => pd.ParameterType).ToArray(),
                        args,
                        out parameters))
                return; //exit on failed validation of parameters


            //call method using signature -- this is where I'm getting an error
            Expression.Call(method, parameters);
        }


I would also be happy with the ability to "assemble" a method call, like in this pseudocode:
C#:
MethodCall mc = new MethodCall(methodInfo);
mc.ClassInstance = this;
mc.Parameters.AddRange(parameters);
mc.Execute();

Thank you in advance for any help or guidance you can provide!

Daniel

Just in Case: The Validation Method

C#:
        //validates that types are as expected.
        //only accepts string, int and decimal
        private bool GetAndValidateParams(Type[] expected, object[] args, out object[] convertedArgs)
        {
            Console.Write("Validating parameters...");


            //check for any unsupported types
            convertedArgs = null;
            Type[] supportedTypes = new Type[] { typeof(decimal), typeof(string), typeof(int) };
            if (expected.Any(t => !supportedTypes.Any(st => t == st))) return false;
            
            if (expected == null) return false;
            if (expected.Count() == 0 && (args == null || args.Count() == 0))
            {
                Console.Write("Success.\r\n");
                return true;
            }
            convertedArgs = new object[args.Count()];
            for (int x = 0; x < expected.Count(); x++)
            {
                //check to make sure there are args left
                if (x > (args.Count() - 1)) return false;


                //if we have an arg and an expectation, check them
                try { convertedArgs[x] = Convert.ChangeType(args[x], expected[x]); }
                catch {return false;}
            }


            //If we got here, everything was validated successfully
            Console.Write("Success.\r\n");
            return true;
        }
 
Looks like you should use MethodInfo.Invoke.
 
You, John, are a brilliant, brilliant man. I can't believe I sank so many hours into that. My final code:

C#:
       public void Execute(string methodName, object[] args)
        {   
            //get called method
            MethodInfo mi = GetMethodByName(methodName);
            if (mi == null) return;


            //validate parameters            
            var paramDefinitions = mi.GetParameters();
            object[] parameters;
            if (!ValidateParams(paramDefinitions.Select(pd => pd.ParameterType).ToArray(), args, out parameters))
                return;


            //execute
            try
            {
                mi.Invoke(this, parameters);
                return string.Empty;
            }
            catch { return; }
        }
 
Back
Top Bottom