Вызов метода класса по имени

219
08 декабря 2016, 22:43

Необходимо вызывать методы класса по имени (т.е. мне известна строка с именем метода класса). Нашел только один более менее подходящий способ - использовать рефлексию.

public class Simple
{
    public string SimpleMethod(string param1)
    {
        return param1;
    }
}
//и собственно вызов 
Simple simple = new Simple();
var method = typeof(Simple).GetMethod("SimpleMethod");
object[] param = new object[] {"Hello World!"};
string result = method.Invoke(sample, param) as string;

Но как я понимаю от такого способа сильно падает производительность. Есть ли более "элегантное решение"?

Answer 1

Тут предлагают использовать System.Linq.Expressions:

namespace Research.UnitTests
{
    using System;
    using Microsoft.VisualStudio.TestTools.UnitTesting;
    using System.Linq;
    using System.Linq.Expressions;
    using System.Collections.Generic;
    using System.Reflection;
    [TestClass]
    public class Research
    {
        [TestMethod]
        public void TestWithTiming()
        {
            var calculator = new Calculator();           
            //Получить ссылку на метод "Calculate".
            MethodInfo methodInfo = typeof(Calculator).GetMethod(
                "Calculate", 
                new Type[] { typeof(int) });
            //Создать параметр.
            ParameterExpression param =  Expression.Parameter(typeof(int), "i");           
            //Создать "точку вызова".
            var thisParameter = Expression.Constant(calculator);
            //Создать выражение для вызова метода.
            //Если бы метод был статический, то "точка вызова" была бы
            //лишней.
            MethodCallExpression methodCall = Expression.Call(
                thisParameter,
                methodInfo, 
                param);
            //Создать лямбда-выражение.
            Expression<Func<int, string>> lambda = 
                Expression.Lambda<Func<int, string>>(
                    methodCall,
                    new ParameterExpression[] { param }
                    );
            //Откомпилировать лямбда-выражение в Func<>.
            Func<int, string> func = lambda.Compile();
            //Замеряем производительность вызова c использованием рефлексии.
            var watch = new System.Diagnostics.Stopwatch();
            watch.Start();
            for (int i = 0; i < 1000000; i++)
            {
                string result = (string)methodInfo.Invoke(calculator, new object[] { i });
            }
            watch.Stop();
            System.Console.WriteLine(watch.ElapsedMilliseconds);
            //Замеряем производительность вызова по имени.
            watch = new System.Diagnostics.Stopwatch();
            for (int i = 0; i < 1000000; i++)
            {
                string result = func(i);
            }
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);
            //Замеряем производительность прямого вызова.
            watch = new System.Diagnostics.Stopwatch.StartNew();
            for (int i = 0; i < 1000000; i++)
            {
                string result = calculator.Calculate(i);
            }
            watch.Stop();
            Console.WriteLine(watch.ElapsedMilliseconds);
        }
    }
    public class Calculator
    {
        public string Calculate(int i)
        {
            string result = string.Empty;
            //Референсный код.
            DateTime now = DateTime.Now;
            DateTime nextDay = now.AddDays(i);
            result = nextDay.ToString();
            return result;
        }
    }
}

Результат:

1930 (рефлексия)

1530 (expression)

1519 (прямой вызов)

READ ALSO
ContextMenuStrip, как отловить закрытие

ContextMenuStrip, как отловить закрытие

Вот так создаю меню правой кнопки мыши над DataGridView :

197
Несуществующие баги в *.cshtml

Несуществующие баги в *.cshtml

Доброго времени суток!Имеется проект asp

246
Как ускорить процесс перебора файлов

Как ускорить процесс перебора файлов

Здесь перебирает слишком долго, пишу на 35 фрейморке, на 4 хорошо идёт EnumerateDirectories и EnumerateFiles, А мне нужно на 3

263