Home

Awesome

ReflectionEnhance

日本語

Overview

The "ReflectionEnhance" library provides high-speed reflection capabilities.
It can execute faster than the standard .NET System.Reflection.

Verified Operating Environments

EnvironmentVersion
Unity2021.3.38f1, 2022.3.20f1
.Net4.x, Standard 2.1

Performance

Measurement Code in the Editor

Assembly Test Code
Type Test Code
ConstructorInfo Test Code
FieldInfo Test Code
MethodBase Test Code
Enum Test Code

Note: The same measurement code is used after building as well.

Results

Below is an excerpt:

MethodMono(Develop)Mono(Release)IL2CPP(Develop)IL2CPP(Release)
Type.GetField(Legacy)3.80069 sec3.789642 sec6.671875 sec6.617188 sec
Type.GetFieldFast0.009490967 sec0.009613037 sec0.0078125 sec0.0078125 sec
Type.FieldOf0.5515747 sec0.5567932 sec0.171875 sec0.1640625 sec
FieldInfo.GetValue(Legacy)1.1716 sec1.239502 sec0.6152344 sec0.5097656 sec
FieldInfo.GetStaticValueFast0.2137909 sec0.2355499 sec0.3222656 sec0.2871094 sec
StaticFieldPointer.GetValue0.01437378 sec0.01635742 sec0.015625 sec0.01171875 sec
MethodBase.Invoke(Legacy)3.869812 sec4.164734 sec1.865234 sec1.8125 sec
MethodBase.InvokeStaticFast0.240387 sec0.2815857 sec0.296875 sec0.3085938 sec
StaticMethodPointerFunc.Invoke0.04116821 sec0.05160522 sec0.04296875 sec0.03125 sec
Delegate0.0333252 sec0.04013062 sec0.03125 sec0.0234375 sec

Detailed Measurement Results

GetField can virtually eliminate processing time under certain conditions.
The same applies to other GetMember methods.
Using FieldOf also speeds up processing, though not as much as GetFieldFast.
(Additionally, since it doesn't create a cache, it's memory-efficient.)
GetValue shows about a 5x performance improvement, and Invoke about 16x.
Moreover, converting the FieldInfo type to a FieldPointer type results in an 81x improvement.
Converting the MethodBase type to a MethodPointer type results in about a 94x performance increase.
(While the Delegate type is even faster, using the MethodPointer type for instance methods has the advantage of being able to change the target.)

Installation Instructions

Install Dependent Packages

Install the following packages:

Install ReflectionEnhance

  1. Open [Window > Package Manager].
  2. Click [+ > Add package from git URL...].
  3. Enter https://github.com/Katsuya100/ReflectionEnhance.git?path=packages and click [Add].

If It Doesn't Work

The above method may not work in environments where Git is not installed.
Please download the com.katuusagi.reflectionenhance.tgz of the corresponding version from Releases and install it using [Package Manager > + > Add package from tarball...].

If It Still Doesn't Work

Download the Katuusagi.ReflectionEnhance.unitypackage of the corresponding version from Releases and import it into the project via [Assets > Import Package > Custom Package].

Usage

Compatible Methods

Many members with the Fast suffix are provided as compatible methods, as shown below:

//  field = typeof(Vector3).GetField("x");
var field = typeof(Vector3).GetFieldFast("x");

Compatible methods with additional conditions are also provided:

//  x = field.GetValue(vec);
var x = field.GetInstanceValueFast<Vector3, float>(ref vec);

Especially, methods like GetValue and Invoke can specify types via generic arguments for optimization.

FieldPointer/MethodPointer Types

You can implicitly convert from a FieldInfo type to a FieldPointer as follows:

InstanceFieldPointer<Vector3, float> field = typeof(Vector3).GetFieldFast("x");
var x = field.GetValue(ref vec);

The FieldPointer type allows even faster access than the compatible methods of FieldInfo.
A MethodPointer type is also provided.
Unlike a delegate, you can specify the target when invoking:

InstanceMethodPointerAction<Vector3, float, float, float> method = typeof(Vector3).GetMethodFast<float, float, float>("Set")
method.Invoke(ref vec, 1, 2, 3);

Substitution Types

Substitution types are provided so that you can specify ByRef types in generic arguments like in GetMethodFast. You can specify ByRef types in generic arguments using the notation below:

var method = typeof(Dictionary<string, int>).GetMethodFast<string, ByRefLike<int>>("TryGetValue");
int result = 0;
method.InstanceInvoke<string, ByRefLike<int>, bool>("hoge", ByRefLike.As(result));

Supported Substitution types are as follows:

Type NameMeaning
ByRefLike<T>ref T
PointerLike<T>T*
VoidLikevoid
GenericTypeParameterLike<_N>N-th generic type parameter
GenericMethodParameterLike<_N>N-th generic method type parameter

FieldOf/MethodOf

Operators that can retrieve members at high speed are provided.
Although they look like methods, internally they behave very similarly to typeof:

var field = typeof(Vector3).FieldOf("x");
var method = typeof(Vector3).MethodOf<float, float, float, VoidLike>("Set");

Specifying a non-existent member will result in a compile-time error.
Also, since it retrieves directly from the handle, it's memory-efficient.

Convenient Methods

I provide many convenient methods that do not exist in the original reflection API.
Here are some examples:

TypeMethodMeaning
REReflectionGetUsingGenericInstanceTypesRetrieves a list of used GenericInstance types
REReflectionGetUsingArrayTypesRetrieves a list of used array types
REReflectionGetUsingByRefTypesRetrieves a list of used ByRef types
REReflectionGetUsingPointerTypesRetrieves a list of used pointer types
REAssemblyIsReferenceToDetermines if it references an assembly
REAssemblyIsReferenceFromoDetermines if it is referenced by an assembly
TypeGetTypeWithMovedRetrieves a type considering MovedFrom
TypeGetSerializeFieldsRetrieves a list of SerializeFields
TypeIsArrayOfUnitySerializeDetermines if it is a type recognized as an array during Unity serialization
FieldInfoIsSerializeFieldDetermines if it is a SerializeField
MethodBaseIsOverridableDetermines if it is a virtual method
MethodInfoIsOperatorDetermines if it is an operator overload method
MethodInfoIsCompatibleDetermines the compatibility of methods

Tips

Techniques for Speeding Up

It operates faster if all arguments are constants:

// Slow implementation
t.GetFieldFast(memberName);

// Fast implementation
typeof(Vector3).GetFieldFast("x");

Also, it's faster not to cache typeof locally:

// Slow implementation
var t = typeof(Vector3);
t.GetFieldFast(memberName);

// Fast implementation
typeof(Vector3).GetFieldFast(memberName);

Error Checking

GetMember methods perform compile-time existence checks when all arguments are constants:

// This will result in a compile-time error.
typeof(Vector3).GetFieldFast("w");

Reasons for High Speed

ReflectionEnhance optimizes GetMember methods through three stages: StaticExpression, Compile-time Optimization, and Memoization.
First, when all arguments can be resolved at compile time as shown below, they are replaced during compilation through StaticExpression optimization:

var field = typeof(Vector3).GetFieldFast("x");
// -> var field = $$StaticTable_0.result;

Even when compile-time resolution is not possible, dynamic optimization through Memoization is applied.

Next, when the type can be resolved at compile time, it is replaced with a function using generic arguments:

var field = typeof(Vector3).GetFieldFast(name);
// -> var field = REReflection.GetFieldFast<Vector3>(name);

Since Memoization operates faster with fewer arguments, this optimization speeds up processing.

Furthermore, FieldInfo.GetValue methods perform special optimizations. For instance fields, they access fields quickly by using offset addresses. For static fields, they manipulate variables directly by obtaining their addresses. In addition to fast processing, they avoid boxing by not going through the object type.

MethodBase.Invoke methods use native function pointers for processing. This allows for direct calls and also avoids boxing.

These techniques have succeeded in significantly speeding up reflection operations.