Maison >développement back-end >Tutoriel C#.Net >Exemple de didacticiel d'appel direct et d'appel réfléchi
Beaucoup de gens disent que l'utilisation de la réflexion entraînera des problèmes de performances. Dans quelle mesure cela sera-t-il plus lent que de l'appeler directement ?
Écrivons une démo pour vérifier la différence de performances entre l'appel direct et l'appel réfléchi. Le code est le suivant :
1 namespace ConsoleApplication7 2 { 3 class Program 4 { 5 static void Main(string[] args) 6 { 7 //比较直接调用和反射调用的性能差异 8 //7ms vs 365ms 9 int times = 1000000;10 var program = new Program();11 CodeTimerHelper.Initialize();12 13 CodeTimerHelper.Time("直接调用", times, () =>14 {15 program.Call();16 });17 18 var t = typeof(Program);19 var obj = Activator.CreateInstance(t);20 CodeTimerHelper.Time("反射调用", times, () =>21 {22 t.InvokeMember("Call", BindingFlags.InvokeMethod, null, obj, null);23 });24 25 Console.ReadKey();26 }27 28 /// <summary>29 /// 测试方法30 /// </summary>31 public void Call()32 {33 }34 35 }36 }
Il existe une différence d'un ordre de grandeur en termes de performances entre les deux.
Pourquoi y a-t-il une perte de performance en réflexion Puisqu'il y a une perte en performance de réflexion, où est la perte spécifique ? 1. La réflexion est basée sur l'assemblage et les métadonnées. Lors de l'utilisation de la réflexion, les métadonnées seront recherchées et les métadonnées sont basées sur des chaînes et ne peuvent pas être précompilées, cette série d'opérations a donc un impact. 2. Un grand nombre de boxing et unboxing ont également un impact sur les performances. Comme nous ne connaissons pas le type de cible et que les paramètres passés à la méthode sont généralement de type objet, il y aura beaucoup de boxing et de unboxing. Solution d'optimisation des performances de réflexionNous savons déjà qu'il existe des problèmes de performances liés à l'utilisation de la réflexion, mais dans certains scénarios, nous devons utiliser la technologie de réflexion, nous devons donc trouver des moyens pour optimiser les performances de réflexion. Nous citons ici la classe d'arbre d'expression System.Linq.Expressions.Expression1 //3,基于表达式树2 var methodInfo = t.GetMethod("Call");3 var executor = new DynamicMethodExecutor(methodInfo);4 CodeTimerHelper.Time("Dynamic executor", times, () =>5 {6 executor.Execute(obj, null);7 });
Résultats des tests :
Wow, pour le même million d'appels, les performances d'utilisation de DynamicMethodExecutor sont presque les mêmes que appel direct.
Ci-joint le code d'encapsulation de DynamicMethodExecutor :
1 /// <summary> 2 /// 3 /// </summary> 4 public class DynamicMethodExecutor 5 { 6 private Func<object, object[], object> m_execute; 7 8 public DynamicMethodExecutor(MethodInfo methodInfo) 9 {10 this.m_execute = this.GetExecuteDelegate(methodInfo);11 }12 13 public object Execute(object instance, object[] parameters)14 {15 return this.m_execute(instance, parameters);16 }17 18 private Func<object, object[], object> GetExecuteDelegate(MethodInfo methodInfo)19 {20 // parameters to execute21 ParameterExpression instanceParameter = Expression.Parameter(typeof(object), "instance");22 ParameterExpression parametersParameter = Expression.Parameter(typeof(object[]), "parameters");23 24 // build parameter list25 List<Expression> parameterExpressions = new List<Expression>();26 ParameterInfo[] paramInfos = methodInfo.GetParameters();27 for (int i = 0; i < paramInfos.Length; i++)28 {29 // (Ti)parameters[i]30 BinaryExpression valueObj = Expression.ArrayIndex(parametersParameter, Expression.Constant(i));31 UnaryExpression valueCast = Expression.Convert(valueObj, paramInfos[i].ParameterType);32 parameterExpressions.Add(valueCast);33 }34 35 // non-instance for static method, or ((TInstance)instance)36 Expression instanceCast = methodInfo.IsStatic ? null : Expression.Convert(instanceParameter, methodInfo.ReflectedType);37 38 // static invoke or ((TInstance)instance).Method39 MethodCallExpression methodCall = Expression.Call(instanceCast, methodInfo, parameterExpressions);40 41 // ((TInstance)instance).Method((T0)parameters[0], (T1)parameters[1], ...)42 if (methodCall.Type == typeof(void))43 {44 Expression<Action<object, object[]>> lambda = Expression.Lambda<Action<object, object[]>>(methodCall, instanceParameter, parametersParameter);45 Action<object, object[]> execute = lambda.Compile();46 return (instance, parameters) =>47 {48 execute(instance, parameters);49 return null;50 };51 }52 else53 {54 UnaryExpression castMethodCall = Expression.Convert(methodCall, typeof(object));55 Expression<Func<object, object[], object>> lambda = Expression.Lambda<Func<object, object[], object>>(castMethodCall, instanceParameter, parametersParameter);56 return lambda.Compile();57 }58 }
En plus de la méthode d'utilisation de l'arborescence d'expression Linq pour générer Delegate, il y a Il existe également des méthodes telles que CodeDom génère du code et le compile dynamiquement, ou utilise Emit pour écrire IL directement afin d'améliorer les performances de réflexion, mais relativement parlant, la méthode ci-dessus est la plus simple.
À ce stade, le résumé de l'ensemble de la réflexion est terminé !
Article de référence
Série de révision des connaissances de base en C# 15 : Réflexion
2. Qu'est-ce que la réflexion et ce que la réflexion peut faire
Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!