Utiliser la réflexion pour invoquer des méthodes non connues à la compilation peut être problématique en terme de performances dans des applications critiques. L’exécution de ces méthodes est environ 2,5 à 3 fois plus lente qu’un appel directe.
Voici un exemple :
class Program { [DllImport("kernel32.dll")] static extern void QueryPerformanceCounter(ref long ticks); static PropertyInfo _intProp = typeof(Foo).GetProperty("IntProp", BindingFlags.Public | BindingFlags.Instance); static void Main(string[] args) { Foo foo = new Foo { IntProp = 10 }; const int COUNT = 1; Console.WriteLine(Measure(() => ReadPropertyWithReflection(foo), COUNT)); Console.WriteLine(Measure(() => ReadPropertyDirectly(foo), COUNT)); } static void ReadPropertyWithReflection(Foo foo) { int intProp = (int)_intProp.GetValue(foo, null); } static void ReadPropertyDirectly(Foo foo) { int intProp = foo.IntProp; } static long Measure(Action action, int count) { long startTicks = 0; QueryPerformanceCounter(ref startTicks); for (int i = 0; i < count; i++) { action(); } long endTicks = 0; QueryPerformanceCounter(ref endTicks); return endTicks - startTicks; } class Foo { public int IntProp { get; set; } } }
Et voici les résultats :
Type d’accès | unités CPU |
Invocation par accès directe | 796 |
Invocation par Réflexion | 1986 |
Ainsi, l’utilisation de la réflexion pour lire une propriété est 2,5 fois plus lente que l’accès directe à cette propriété.
Les méthodes dynamiques peuvent être utilisées pour générer et exécuter une méthode à l’exécution sans devoir déclarer une assemblie dynamique et un type dynamique qui contiendront la méthode. Elles représentent un moyen plus efficace pour générer et exécuter ce type de code.
Voici un exemple d’utilisation de la classe de méthode dynamique (DynamicMethod) pour générer un accès en lecture à la propriété.
static Func<Arg0, TReturn> EmitGetter<Arg0, TReturn>(PropertyInfo propertyInfo) { MethodInfo mi = propertyInfo.GetGetMethod(); DynamicMethod dm = new DynamicMethod( "_get", typeof(TReturn), new Type[] { typeof(Arg0) }, propertyInfo.DeclaringType); ILGenerator il = dm.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Castclass, propertyInfo.DeclaringType); il.EmitCall(OpCodes.Callvirt, mi, null); il.Emit(OpCodes.Ret); return (Func<Arg0, TReturn>)dm.CreateDelegate(typeof(Func<Arg0, TReturn>)); }
Utilisons maintenant cette méthode pour définir un accès en lecture à PropertyInfo en exécution et retourner le délégué.
Func<Foo, int> getter = EmitGetter<Foo, int>(_intProp); Console.WriteLine(Measure(() => getter(foo), COUNT));
Et voici les résultats obtenus avec les 3 type d’accès différents pour lire la valeur de la propriété :
Type d’accès | unités CPU |
Invocation par accès directe | 796 |
Invocation avec Dynamic method | 1190 |
Invocation par Réflexion | 1986 |