C#反射的性能究竟如何?
2 年前
· 来自专栏
Unity游戏开发入门
反射允许我们在程序运行时做一些非常动态的操作,如通过以字符串的形式调用函数。因此,当程序需要非常高的灵活性时,这是一个非常强大的工具。但是,它真的非常非常的慢。今天我们将测试反射与常规代码的性能差异到底有多大。你还可以了解到如何使用C#反射。
C#反射操作大多位于System.Reflection命名空间中,但反射操作是基于System.Type类的。每个变量在C#中都有一个Type字段,你可以通过以下两种方式获得:
// get the Type of class
var classType = typeof(TestClass);
// get the Type of variable
var classType = classInstance.GetType();
获取到Type之后就能获取它的字段,属性,方法等等。这些信息都被定义在System.Reflection命名空间中以”Info”结尾的类中,如:FieldInfo,PropertyInfo,MethodInfo等。下面是获取它们的方式:
// Get the field named "MyField"
var fieldInfo = classType.GetField("MyField");
// Get the property named "MyProperty";
var propertyInfo = classType.GetProperty("MyProperty");
// Get the method/function named "MyMethod";
var methodInfo = classType.GetMethod("MyMethod");
一旦获取到这些,就可以像直接编写代码一样调用Info中的方法来操作对象。可以读取和写入字段值,属性以及调用方法。下面是这些操作的使用方式:
// Read and or write a field from an instance of TestClass
fieldInfo.GetValue(classInstance);
fieldInfo.SetValue(classInstance, 123);
// Read and or write a property from an instance of TestClass
// The last parameter is null for non-indexed properties
propertyInfo.GetValue(classInstance, null);
propertyInfo.SetValue(classInstance, 123, null);
// Call a method on an instance of TestClass
var paramters = new object[] { 1, 2, 3 };
methodInfo.Invoke(classInstance, paramters);
接下来,我们来看一下将反射与直接调用作比较的简单测试。
using System;
using System.Diagnostics;
using System.Reflection;
using System.Text;
using UnityEngine;
public static class StopwatchExtensions
public delegate void TestFunction();
public static long RunTest(this Stopwatch stopwatch, TestFunction testFunc)
stopwatch.Reset();
stopwatch.Start();
testFunc();
return stopwatch.ElapsedMilliseconds;
public class TestClass
public int intField;
public int intProperty { get; set; }
public void VoidMethod() { }
public class Test : MonoBehaviour
private const int _numIterations = 10000000;
private StringBuilder _reportBuilder = new StringBuilder(200);
private string _report = string.Empty;
private Rect _drawRect = Rect.zero;
private void Awake()
_drawRect = new Rect(0, 0, Screen.width, Screen.height);
private void Start()
var stopwatch = new Stopwatch();
Type testClassType = null;
FieldInfo fieldInfo = null;
PropertyInfo propertyInfo = null;
MethodInfo methodInfo = null;
var intValue = 0;
var testClassInstance = new TestClass();
var getTypeTime = stopwatch.RunTest(() => {
for (int index = 0; index < _numIterations; ++index)
testClassType = typeof(TestClass);
var getFieldTime = stopwatch.RunTest(()=> {
for (int index = 0; index < _numIterations; ++index)
fieldInfo = testClassType.GetField("intField");
var getPropertyTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
propertyInfo = testClassType.GetProperty("intProperty");
var getMethodTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
methodInfo = testClassType.GetMethod("VoidMethod");
var readFieldReflectionTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
intValue = (int)fieldInfo.GetValue(testClassInstance);
var readPropertyReflectionTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
intValue = (int)propertyInfo.GetValue(testClassInstance, null);
var readFieldDirectTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
intValue = testClassInstance.intField;
var readPropertyDirectTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
intValue = testClassInstance.intProperty;
var writeFieldReflectionTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
fieldInfo.SetValue(testClassInstance, 5);
var writePropertyReflectionTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
propertyInfo.SetValue(testClassInstance, 5, null);
var writeFieldDirectTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
testClassInstance.intField = 5;
var writePropertyDirectTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
testClassInstance.intProperty = 5;
var callMethodReflectTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
methodInfo.Invoke(testClassInstance, null);
var callMethodDirectTime = stopwatch.RunTest(() =>
for (int index = 0; index < _numIterations; ++index)
testClassInstance.VoidMethod();
_reportBuilder.AppendLine("Test, Reflection Time, Direct Time");
_reportBuilder.AppendFormat("Get Type, {0} ,0\n", getTypeTime);
_reportBuilder.AppendFormat("Get Field, {0} ,0\n", getFieldTime);
_reportBuilder.AppendFormat("Get Property, {0} ,0\n", getPropertyTime);
_reportBuilder.AppendFormat("Get Method, {0} ,0\n", getMethodTime);
_reportBuilder.AppendFormat("Read Field, {0}, {1}\n", readFieldReflectionTime, readFieldDirectTime);
_reportBuilder.AppendFormat("Read Property, {0}, {1}\n", readPropertyReflectionTime, readPropertyDirectTime);
_reportBuilder.AppendFormat("Write Field, {0}, {1}\n", writeFieldReflectionTime, writeFieldDirectTime);
_reportBuilder.AppendFormat("Write Property, {0}, {1}\n", writePropertyReflectionTime, writePropertyDirectTime);
_reportBuilder.AppendFormat("Call Method, {0}, {1}\n", callMethodReflectTime, callMethodDirectTime);
_report = _reportBuilder.ToString();