Viele Leute sagen, dass die Verwendung von Reflektion zu Leistungsproblemen führen wird, als sie direkt aufzurufen.
Lassen Sie uns eine Demo schreiben, um den Leistungsunterschied zwischen direktem Anruf und reflektierendem Anruf zu überprüfen. Der Code lautet wie folgt:
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 }
Testergebnisse:
Nach den Ergebnissen von 1 Million Anrufen zu urteilen, ist es tatsächlich so, wie viele Leute sagten, Es gibt einen Leistungsunterschied zwischen den beiden Größenordnungen.
Da es einen Verlust bei der Reflexionsleistung gibt, wo liegt der spezifische Verlust?
1. Bei der Verwendung von Reflection werden Metadaten durchsucht, und Metadaten basieren auf Zeichenfolgen und können nicht vorkompiliert werden. Daher hat diese Reihe von Vorgängen Auswirkungen.
2. Auch viele Ein- und Auspackvorgänge wirken sich auf die Leistung aus. Da wir den Zieltyp nicht kennen und die an die Methode übergebenen Parameter normalerweise vom Typ Objekt sind, wird es viel Ein- und Auspacken geben.
Wir wissen bereits, dass es Leistungsprobleme bei der Verwendung von Reflection gibt, aber in einigen Szenarien müssen wir Reflection-Technologie verwenden, also müssen wir Wege finden um die Reflexionsleistung zu optimieren.
Hier zitieren wir die von Lao Zhao offengelegte Ausdrucksbaumklasse System.Linq.Expressions.Expression und vergleichen sie mit dem direkten Aufruf:
1 //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 });
Testergebnisse:
Wow, bei den gleichen 1 Million Anrufen ist die Leistung bei der Verwendung von DynamicMethodExecutor fast gleich direkter Anruf.
Angehängt ist der Kapselungscode von 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 }
Zusätzlich zur Methode zur Verwendung des Linq-Ausdrucksbaums zum Generieren von Delegate gibt es dort Es gibt auch Methoden wie CodeDom, der Code generiert und dynamisch kompiliert, oder Emit verwendet, um IL direkt zu schreiben, um die Reflexionsleistung zu verbessern. Relativ gesehen ist die obige Methode jedoch die einfachste.
An diesem Punkt ist die Zusammenfassung der gesamten Reflexion abgeschlossen!
Direkter Methodenaufruf, Reflexionsaufruf und ... Lambda-Ausdrucksaufruf
C#-Grundwissensüberprüfungsreihe 15: Reflexion
2. Was ist Reflexion und was kann Reflexion bewirken
Das obige ist der detaillierte Inhalt vonBeispiel-Tutorial für direkte Anrufe vs. reflektierende Anrufe. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!