admin 发表于 2022-6-29 13:06:16

C#不同反射调用方法的性能差异

using System;
using System.Diagnostics;
using System.Reflection;

namespace Walterlv.Demo
{
    public class Program
    {
      static void Main(string[] args)
      {
            // 调用的目标实例。
            var instance = new StubClass();

            // 使用反射找到的方法。
            var method = typeof(StubClass).GetMethod(nameof(StubClass.Test), new[] { typeof(int) });
            // 将反射找到的方法创建一个委托。
            var func = InstanceMethodBuilder<int, int>.CreateInstanceMethod(instance, method);

            // 跟被测方法功能一样的纯委托。
            Func<int, int> pureFunc = value => value;

            // 测试次数。
            var count = 10000000;

            // 直接调用。
            var watch = new Stopwatch();
            watch.Start();
            for (var i = 0; i < count; i++)
            {
                var result = instance.Test(5);
            }

            watch.Stop();
            Console.WriteLine($"{watch.Elapsed} - {count} 次 - 直接调用");

            // 使用同样功能的 Func 调用。
            watch.Restart();
            for (var i = 0; i < count; i++)
            {
                var result = pureFunc(5);
            }

            watch.Stop();
            Console.WriteLine($"{watch.Elapsed} - {count} 次 - 使用同样功能的 Func 调用");

            // 使用反射创建出来的委托调用。
            watch.Restart();
            for (var i = 0; i < count; i++)
            {
                var result = func(i);
            }

            watch.Stop();
            Console.WriteLine($"{watch.Elapsed} - {count} 次 - 使用反射创建出来的委托调用");

            // 使用反射得到的方法缓存调用。
            watch.Restart();
            for (var i = 0; i < count; i++)
            {
                var result = method.Invoke(instance, new object[] { 5 });
            }

            watch.Stop();
            Console.WriteLine($"{watch.Elapsed} - {count} 次 - 使用反射得到的方法缓存调用");

            // 直接使用反射调用。
            watch.Restart();
            for (var i = 0; i < count; i++)
            {
                var result = typeof(StubClass).GetMethod(nameof(StubClass.Test), new[] { typeof(int) })
               ?.Invoke(instance, new object[] { 5 });
            }

            watch.Stop();
            Console.WriteLine($"{watch.Elapsed} - {count} 次 - 直接使用反射调用");
      }

      private class StubClass
      {
            public int Test(int i)
            {
                return i;
            }
      }
    }
    public static class InstanceMethodBuilder<T, TReturnValue>
    {
      /// <summary>
      /// 调用时就像 var result = func(t)。
      /// </summary>
      public static Func<T, TReturnValue> CreateInstanceMethod<TInstanceType>(TInstanceType instance, MethodInfo method)
      {
            if (instance == null) throw new ArgumentNullException(nameof(instance));
            if (method == null) throw new ArgumentNullException(nameof(method));

            return (Func<T, TReturnValue>)method.CreateDelegate(typeof(Func<T, TReturnValue>), instance);
      }

      /// <summary>
      /// 调用时就像 var result = func(this, t)。
      /// </summary>
      public static Func<TInstanceType, T, TReturnValue> CreateMethod<TInstanceType>(MethodInfo method)
      {
            if (method == null)
                throw new ArgumentNullException(nameof(method));

            return (Func<TInstanceType, T, TReturnValue>)method.CreateDelegate(typeof(Func<TInstanceType, T, TReturnValue>));
      }
    }
}
页: [1]
查看完整版本: C#不同反射调用方法的性能差异