-
Notifications
You must be signed in to change notification settings - Fork 4
Accessing
The access capability of Fasterflect allows you to perform dynamic invocations such as method invocation, field retrieval and setting, indexer access etc. This feature covers both reference type (including array type) and value type, including struct type. Advanced capability like ref/out, parameter type inference are also supported.
Refer to Standard Access to learn about this capability of Fasterflect. Like the query capability, custom flags are fully supported in all Fasterflect access API to enable maximum flexibility. Finally, you can refer to the Delegate API to achieve maximum invocation performance with Fasterflect. [Working with Structs] requires a little bit of special treatment.
The rest of this page discusses the common conventions for access methods and the underlying dynamic code generation mechanism which enables the great performance of Fasterflect.
Extension methods to System.Type allow you to create instance and access to static members while extension methods to System.Object allow you to access instance members. Besides, Fasterflect also provides extension methods to .NET metadata classes, e.g. FieldInfo, MethodInfo etc. to provide the same invocation capability. There are also extension methods for System.Type that allow you to retrieve generated delegate to perform the invocation. See Delegate API for more details.
For each access method, there is one overload accepting flags. By default, when you use the overload that doesn't accept flags, Fasterflect will search for both public and non-public members whose are either instance or static depending on the context object they are called on (System.Type's extension methods are for static accesses while System.Object's extension methods are for instance accesses).
Some access method require an array of types indicating parameter types of constructors, methods and indexers. When using these methods you can omit this type array if you are sure that the actual argument array contains no null value. In that case, Fasterflect will generate the type array matching the supplied arguments (simply by iterating through the argument array and invoking GetType() on each element) to feed to the underlying engine.
When a method or constructor accepts ref/out parameter, you can employ the convention of passing an object array as the arguments and calling MakeByRefType() on the actual type of the ref/out parameter. For example the method Walk in class Person is defined as internal void Walk( double meters, out double metersTravelled ). You can invoke this method and retrieve the out value as follow: var person = new Person(); var arguments = new object[] { 10d, null }; person.CallMethod( "Walk", new[] { typeof(double), typeof(double).MakeByRefType() }, arguments ); Assert.AreEqual( person.GetFieldValue( "metersTravelled" ), arguments[ 1 ] );
How Great Performance Is Achieved by Fasterflect
The access engine of Fasterflect is built based on .NET 2.0's Dynamic Methods which allow methods to be built at runtime using the Common Intermediate Language (CIL). After being built, dynamic methods can be casted into delegates and invoked just like normal CLR delegates.
Let's discuss a bit of the background to see how such a feature can help with the development of Fasterflect. Let's say we want to invoke a method on an object whose type we cannot (or do not want to) compile with at compile-time. We can do that using the built-in .NET Reflection API, something like this:
string someTypeName = …; // load the type name from a config file
Type type = Assembly.GetExecutingAssembly().GetType(someTypeName);
string methodToBeInvoked = …; // load method name from a config file
MethodInfo info = type.GetMethod(methodToBeInvoked, BindingFlags.Instance);
object result = info.Invoke(Activator.CreateInstance(type));
This works. But the performance of such Reflection calls is very poor, especially when we start executing the methods many times, a common scenario in most applications using Reflection. To achieve better performance, we can generate code at runtime. Get back to our example, the idea is that by the time someTypeName and methodToBeInvoked are already initialized with values from a config file at runtime (let's say the type name is Person and the method name is GetName), we already have enough information to construct a non-reflective piece of code like below. (Assume GenericInvocator is an interface with just one method, object GenericInvoke(object), that we already coded.)
public class ConcreteInvocator : GenericInvocator
{
public object Invoke(object target)
{
var person = (Person)target;
return target.GetName();
}
}
After generating the code at runtime, we can compile it on the fly using either the csc.exe compiler or a CodeDomProvider. Assume we compile the code into an assembly, we can load and perform the invocation like below:
object obj = Assembly.LoadFrom(generatedDll).CreateInstance("ConcreteInvocator")
GenericInvocator personInvocator = obj as GenericInvocator;
string name = (string)personInvocator.Invoke(personObject);
Notice that we only need to generate the code once, and then can reuse it for all subsequent invocations, which will be very fast because it's nothing but the direct call to person.GetName().
That is the idea. However, generating and compiling high-level code (or building a CodeDOM tree) is not the only option. Another option is generating CIL directly and skipping all the compilation overhead. We can do that either by using classes in the Reflection Emit namespace to build a .NET assembly, module, and type at runtime, or by using Dynamic Methods. Among all these options, Dynamic Method is selected for the following reasons:
- Dynamic Methods can be declared to skip visibility. If a member is declared as private, the body of a dynamic method can still access it directly instead of via Reflection. This is not the case with the alternatives.
- We don't need to write code to build modules, types etc.; instead, we can just write code for Dynamic Methods, and .NET takes care of the rest.
- Dynamic Methods can be garbage-collected while methods built with the alternatives cannot be unloaded once loaded into the app-domain. (Refer to this page to see how is fact is employed within Fasterflect to build a memory-sensitive library.)
The code to generate a dynamic method invoking a no-argument instance method of a specific type would look something like below. (Assume the delegate MethodInvoker is already declared as public delegate object MethodInvoker(object target);.)
public MethodInvoker CreateDelegate(Type targetType, string methodName)
{
var method= new DynamicMethod("invoke", MethodAttributes.Static |
MethodAttributes.Public, CallingConventions.Standard,
typeof(object), new Type[0], targetType, true);
ILGenerator generator = method.GetILGenerator();
MethodInfo methodInfo = targetType.GetMethod(methodName, BindingFlags.Instance);
generator.Emit(OpCodes.Ldarg_0);
generator.Emit(OpCodes.Castclass, targetType);
generator.Emit(OpCodes.Callvirt, methodInfo);
if (methodInfo.ReturnType == typeof(void))
{
generator.Emit(OpCodes.Ldnull);
}
else
{
if (methodInfo.ReturnType.IsValueType)
{
generator.Emit(OpCodes.Box, methodInfo.ReturnType);
}
}
generator.Emit(OpCodes.Ret);
return method.CreateDelegate(typeof(MethodInvoker));
}
Now, you can invoke the dynamic method simply via the returned delegate:
MethodInvoker invoker = CreateDelegate(personType, "GetName");
string name = (string)invoker.Invoke(personObject);
That's basically the trick behind the great level of performance achieved by Fasterflect.