Home > .NET, open source > .NET Reflection Made Fast & Simple – Fasterflect 1.1 Release

.NET Reflection Made Fast & Simple – Fasterflect 1.1 Release

November 19th, 2009

I’m pleased to announce that version 1.1 of Fasterflect, the fast & simple .NET reflection invocation library, is already released in CodePlex. The download include Fasterflect binary, code documentation, benchmark application source, sample code, unit test source (95%+ coverage, can be used to learn about all usage aspect of Fasterflect).

For an introduction to Fasterflect, including its design, APIs, and benchmark, please refer to this Code Project article.

Changes since version 1.0 beta (the one documented in the Code Project article) include:

  • Support array types (construction, set element, get element)
  • Support structs
  • Support ref/out parameters
  • Support lookup by covariant parameter type
  • Inference of parameter types for non-null arguments
  • Several bug fixes

Sample Code

class PersonClass
{
    private int id;
    private int milesTraveled;
    public int Id
    {
        get { return id; }
        set { id = value; }
    }
    public string Name { get; private set; }
    private static int InstanceCount;

    public PersonClass() : this(0) {}

    public PersonClass(int id) : this(id, string.Empty) { }

    public PersonClass(int id, string name)
    {
        Id = id;
        Name = name;
        InstanceCount++;
    }

    public char this[int index]
    {
        get { return Name[index]; }
    }

    private void Walk(int miles) 
    {
        milesTraveled += miles;
    }

    private static void IncreaseInstanceCount()
    {
        InstanceCount++;
    }

    private static int GetInstanceCount()
    {
        return InstanceCount;
    }
    
    public static void Swap(ref int i, ref int j)
    {
        int tmp = i;
        i = j;
        j = tmp;
    }
}
struct PersonStruct
{
    private int id;
    private int milesTraveled;
    public int Id
    {
        get { return id; }
        set { id = value; }
    }
    public string Name { get; private set; }
    private static int InstanceCount;

    public PersonStruct(int id) : this(id, string.Empty) { }

    public PersonStruct(int id, string name) : this()
    {
        Id = id;
        Name = name;
        InstanceCount++;
    }

    public char this[int index]
    {
        get { return Name[index]; }
    }

    private void Walk(int miles) 
    {
        milesTraveled += miles;
    }

    private static void IncreaseInstanceCount()
    {
        InstanceCount++;
    }

    private static int GetInstanceCount()
    {
        return InstanceCount;
    }
    
    public static void Swap(ref int i, ref int j)
    {
        int tmp = i;
        i = j;
        j = tmp;
    }
}
class Program
{
    static void Main()
    {
        // Load a type reflectively, just to look like real-life scenario
        var types = new[]
                        {
                            Assembly.GetExecutingAssembly().GetType("FasterflectSample.PersonClass"),
                            Assembly.GetExecutingAssembly().GetType("FasterflectSample.PersonStruct")
                        };
        Array.ForEach(types, type =>
                                 {
                                     ExecuteNormalApi(type);
                                     ExecuteCacheApi(type);
                                 });
    }

    private static void ExecuteNormalApi(Type type)
    {
        bool isStruct = type.IsValueType;

        // Person.InstanceCount should be 0 since no instance is created yet
        AssertTrue(type.GetField<int>("InstanceCount") == 0);
        
        // Invokes the no-arg constructor
        object obj = type.Construct();

        // Double-check if the constructor is invoked successfully or not
        AssertTrue(null != obj);

        // struct's no-arg constructor cannot be overriden, thus the following checking
        // is not applicable to struct type
        if (!isStruct)
        {
            // Now, Person.InstanceCount should be 1
            AssertTrue(1 == type.GetField<int>("InstanceCount"));

            // What if we don't know the type of InstanceCount?  
            // Just specify object as the type parameter
            AssertTrue(type.GetField<object>("InstanceCount") != null);
        }

        // We can bypass the constructor to change the value of Person.InstanceCount
        type.SetField("InstanceCount", 2);
        AssertTrue(2 == type.GetField<int>("InstanceCount"));

        // Let's invoke Person.IncreaseCounter() static method to increase the counter
        // In fact, let's chain the calls to increase 2 times
        type.Invoke("IncreaseInstanceCount")
            .Invoke("IncreaseInstanceCount");
        AssertTrue(4 == type.GetField<int>("InstanceCount"));

        // Now, let's retrieve Person.InstanceCount via the static method GetInstanceCount
        AssertTrue(4 == type.Invoke<int>("GetInstanceCount"));

        // If we're not interested in the return (e.g. only in the side effect), 
        // we don't have to specify the type parameter (and can chain the result).  
        AssertTrue(4 == type.Invoke("GetInstanceCount")
                            .Invoke("GetInstanceCount")
                            .Invoke<int>("GetInstanceCount"));

        // Invoke method receiving ref/out params, we need to put arguments in an array
        var arguments = new object[] { 1, 2 };
        type.Invoke("Swap", 
            // Parameter types must be set to the appropriate ref type
            new[] { typeof(int).MakeByRefType(), typeof(int).MakeByRefType() },
            arguments);
        AssertTrue(2 == (int)arguments[0]);
        AssertTrue(1 == (int)arguments[1]);

        // Now, invoke the 2-arg constructor.  We don't even have to specify parameter types
        // if we know that the arguments are not null (Fasterflect will internally retrieve type info).
        obj = type.Construct(1, "Doe");

        // Due to struct type's pass-by-value nature, in order for struct to be used 
        // properly with Fasterflect, you need to convert it into a holder (wrapper) first.  
        // The call below does nothing if obj is reference type so when unsure, just call it.
        obj = obj.CreateHolderIfValueType();

        // id and name should have been set properly
        AssertTrue(1 == obj.GetField<int>("id"));
        AssertTrue("Doe" == obj.GetProperty<string>("Name"));

        // Let's use the indexer to retrieve the character at index 1
        AssertTrue('o' == obj.GetIndexer<char>(1));

        // If there's null argument, or when we're unsure whether there's a null argument
        // we must explicitly specify the param type array
        obj = type.Construct(new[] { typeof(int), typeof(string) }, new object[] { 1, null })
            .CreateHolderIfValueType();

        // id and name should have been set properly
        AssertTrue(1 == obj.GetField<int>("id"));
        AssertTrue(null == obj.GetProperty<string>("Name"));

        // Now, modify the id
        obj.SetField("id", 2);
        AssertTrue(2 == obj.GetField<int>("id"));
        AssertTrue(2 == obj.GetProperty<int>("Id"));

        // We can chain calls
        obj.SetField("id", 3).SetProperty("Name", "Buu");
        AssertTrue(3 == obj.GetProperty<int>("Id"));
        AssertTrue("Buu" == obj.GetProperty<string>("Name"));
         
        // How about modifying both properties at the same time using an anonymous sample
        obj.SetProperties(new {
                                  Id = 4, 
                                  Name = "Nguyen"
                              });
        AssertTrue(4 == obj.GetProperty<int>("Id"));
        AssertTrue("Nguyen" == obj.GetProperty<string>("Name"));

        // Let's have the folk walk 6 miles (and try chaining again)
        obj.Invoke("Walk", 1).Invoke("Walk", 2).Invoke("Walk", 3);

        // Double-check the current value of the milesTravelled field
        AssertTrue(6 == obj.GetField<int>("milesTraveled"));

        // Construct an array of 10 elements for current type
        var arr = type.MakeArrayType().Construct(10);

        // Get & set element of array
        obj = type.Construct();
        arr.SetElement(4, obj).SetElement(9, obj);

        if (isStruct) // struct, won't have same reference
        {
            AssertTrue(obj.Equals(arr.GetElement<object>(4)));
            AssertTrue(obj.Equals(arr.GetElement<object>(9)));
        }
        else 
        {
            AssertTrue(obj == arr.GetElement<object>(4));
            AssertTrue(obj == arr.GetElement<object>(9));
        }

        // Remember, struct array doesn't have null element 
        // (instead always initialized to default struct)
        if (!isStruct)
        {
            AssertTrue(null == arr.GetElement<object>(0));
        }
    }

    private static void ExecuteCacheApi(Type type)
    {
        var range = Enumerable.Range(0, 10).ToList();

        // Let's cache the getter for InstanceCount
        StaticAttributeGetter count = type.DelegateForGetStaticField("InstanceCount");

        // Now cache the 2-arg constructor of Person and playaround with the delegate returned
        int currentInstanceCount = (int)count();
        ConstructorInvoker ctor = type.DelegateForConstruct(new[] { typeof(int), typeof(string) });
        range.ForEach(i =>
        {
            object obj = ctor(i, "_" + i).CreateHolderIfValueType();
            AssertTrue(++currentInstanceCount == (int)count());
            AssertTrue(i == obj.GetField<int>("id"));
            AssertTrue("_" + i == obj.GetProperty<string>("Name"));
        });

        // Whatever thing we can do with the normal API, we can do with the cache API.
        // For example:
        AttributeSetter nameSetter = type.DelegateForSetProperty("Name");
        AttributeGetter nameGetter = type.DelegateForGetProperty("Name");

        object person = ctor(1, "Buu").CreateHolderIfValueType();
        AssertTrue("Buu" == nameGetter(person));
        nameSetter(person, "Doe");
        AssertTrue("Doe" == nameGetter(person));

        // Another example
        person = type.Construct().CreateHolderIfValueType();
        MethodInvoker walk = type.DelegateForInvoke("Walk", new[] { typeof(int) });
        range.ForEach(i => walk(person, i));
        AssertTrue(range.Sum() == person.GetField<int>("milesTraveled"));
    }

    public static void AssertTrue(bool expression)
    {
        if (!expression)
            throw new Exception("Not true");
        Console.WriteLine("Ok!");
    }
}
  1. November 20th, 2009 at 00:10 | #1

    Thanks for this wonderful component. I used your beta version having some issues for the image path from the javascript. I will try with this version and come back to you with example. Another issue i have is, we have inline script for initializing the jquery datepicker plugin due to partial postback because $document.ready() will not fire on partial postback. In this case still the image paths are still in the partial view and not in the combres xml. Do you have any plan to implement this?

    Thanks,
    Rajan R.G

  2. November 20th, 2009 at 10:59 | #2

    @Rajan
    I suppose you commented about Combres (http://www.buunguyen.net/blog/combres-webform-mvc-client-side-resource-combine-library.html), not Fasterflect (this post is about Fasterflect) :) .

    Combres 1.0 does address issues with CSS image paths. However, I have not implemented a filter for JavaScript image paths yet. Can you send the specific path issues you have with JavaScript?

    I don’t quite understand the issue you have with partial views though, can you send me more description & code sample if possible?

    If you can, please post them to the Combres’ forum http://combres.codeplex.com/Thread/List.aspx.

  3. November 24th, 2009 at 06:35 | #3

    Upgrade it so it uses expression trees instead of magic strings and it will be in all my projects that use reflection!

  4. November 24th, 2009 at 14:01 | #4

    @Chris:
    Thanks for the suggestion. In fact, I did think about using expression tree for type-safe naming but decided not to. The problem is that having such type-safety would require the calling call to pass the type parameter to the Fasterflect method call, while in most reflection scenarios, people do not know that specific type until runtime. In the following example, developers need to specify the type parameter if wanting to use type-safe name:

    object obj = LoadFromAssembly(...);
    // Or IWalkable if base interface is known
    obj.Invoke (p => p.Go(2));

    On the other hand, if people already know the type at compile-time, I suppose it’s better they just perform direct invocation instead of Fasterflect invocation, like:

    // Or IWalkable if base interface is known
    Person p = (Person) LoadFromAssembly(...);
    p.Go(2);

    Or do I miss any good scenario?

  5. Hugh
    December 13th, 2009 at 22:04 | #5

    Hi

    This looks like very interesting work, but I am a little puzzled. We analyze millions of structs on the fly in one app. Part of our processing is to dynamically extract field values at runtime (currently using reflection) for some fields (we have the ‘FieldInfos’ cached).

    Because these are structs we would need to invoked your ‘CreateHolderIfValueType’ on every struct before we can get at the field value (using an AttributeGetter). When I tested this out it looked as though the cost of this was high. The wrapping operation is far more expensive than the call to the AttributeGetter delegate!

    Thus for structs the whole process is far slower and this is puzzling me.

    Since a struct is a value type, why is it not possible to simply get its address and then just set the field?

    Why do we need any ‘wrapping’?

    Could we not pass the struct by ref perhaps?

    I’ve seen some IL scattered around the web that uses DynamicMethod to handle struct refelction and it looks like it should be very fast.

    Am I doing something wrong with your library or is there some issue I am not considering in all this?

    Thanks

    Hugh

  6. Hugh
    December 15th, 2009 at 00:06 | #6

    Hi

    There is another issue that could be improved. If a field is an Int32 (for example) but I pass an Int16 into the field set method, then the generated IL raises an invalid cast exception. I’m not sure how the IL should best handle that, but right now one must pass exactly the same type in as the field’s type, else we get a failure.

    Thanks

    Hugh

  7. December 15th, 2009 at 16:30 | #7

    @Huge:
    Passing by ref is definitely an option. However, there are a couple of issues with this approach. First, extension method doesn’t support passing ref for the ‘this’ parameter (e.g. can’t declare s/t like ‘public void Invoke(ref this ValueType obj, object[] params)), thus we can’t have an API that is as nice as the current API. Second, add ‘ref’ to every possible method calls with struct param would double the size of the API (1 API set for reference, another API set for value type). That’s why I went with the wrapper approach: you create the wrapper once, then you can use the struct type with Fasterflect exactly as you use reference type.

    I’m not quite sure why it gives you poor performance though, because I think the overhead of calling CreateHolderIfValueType() would still be not as worse as .NET reflection. If you can post the code segment that is slower than .NET reflection, I would probably be able to understand better.

    Regarding the int16 & int32, can you please post the code that throws the exception?

Comments are closed.