添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接
C#反射的性能究竟如何?

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();