Archive

Posts Tagged ‘reflection’

Fasterflect vs. C# 4.0 Dynamic

December 31st, 2009 Buu Nguyen 4 comments

As .NET 4.0 final release will hit the market pretty soon, it’s worth discussing the value of Fasterflect in the face of C# 4.0’s dynamic keyword.  (If you aren’t familiar with C# 4.0 dynamic, you should read my article on C# 4.0 before continuing.)

To recall, Fasterflect was designed to address 2 key disadvantages of .NET Reflection: ease of use and performance.  As seen in my article about C# 4.0, the dynamic keyword nicely addresses the easy of use with a resulting syntax looking even nicer than Fasterflect.  How about performance?

I improved the benchmark application of Fasterflect to add some benchmark code comparing between the performance of C# 4.0 dynamic and Fasterflect.  Here is result of method invocation benchmark.

fasterflectbenchmark

While performing slower than Fasterflect’s cached API, C# dynamic performs much better than Fasterflect’s standard API.  (Not surprisingly, the C# dynamic binding doesn’t resort to the slow reflection mechanism internally.)

With this awareness in mind, together with the understanding about the features of C# dynamic and Fasterflect, let’s discuss about areas where Fasterflect shines even in the face of C# 4.0 dynamic:

  • C# dynamic doesn’t handle invocations performed based on dynamic information (i.e. field name read from XML file).
  • C# dynamic does handle certain types of dynamic invocations, including static method invocation, static property invocation and constructor invocation.
  • C# dynamic doesn’t handle non-public members.  So you’ll receive exception if trying to, say, invoke a private method.
  • When performance is more critical than readability then the Fasterflect’s cached API might be favored over C# dynamic.

Granted, there should be workarounds for the first 3 items.  For example, if you want to invoke static method dynamically with C# dynamic, you would have to workaround like the approach described in this article.  However, the approach uses reflection behind the scene so you would have the same performance issue to start with.

This doesn’t say that Fasterflect is better than C# dynamic though.  While both share a couple of features and maybe used interchangeably in some scenarios, they also have different problems of their own to address.  There are many things that you can do with C# dynamic that you couldn’t do with Fasterflect, e.g. implementing an interceptor to dynamically handle all method invocations (or missing methods) etc.  So, pick the right tool for your problem at hand.

  • Share/Bookmark
Categories: .NET Tags: , , , ,

.NET Reflection Made Fast & Simple – Fasterflect 1.1 Release

November 19th, 2009 Buu Nguyen 7 comments

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!");
    }
}
  • Share/Bookmark

Fasterflect – a fast and simple API for Reflection invocation

August 10th, 2009 Buu Nguyen 2 comments

If you think the built-in Reflection API in the .NET framework is too verbose for many circumstances and has poor performance, you are not alone. I think that too. And yet, being able to write reflective code is an inevitable (technical) requirement in most of the applications I develop. This is why I have built Fasterflect (read either “Faster-flect” or “Fast-reflect”) as an alternative API to the .NET Reflection functionality.

The goal of this library is to make Reflection calls as straightforward as possible while offering better performance than normal Reflection calls. In this article, I’ll describe the approach chosen to build Fasterflect, its APIs, and measure its performance via some benchmarks.

You can refer to this Code Project page for the guide to the library. For latest development code & binary, refer to the project page in CodePlex.

Updated 8/19/2009: Version 1.0 is released and can be downloaded from the CodePlex project page. This release supports ref/out parameters for methods/constructors/indexers and structs. Implementing support for struct has been quite challenging (and thus interesting) due to its data-copy parameter passing semantic and my design goal of sharing the API for both structs & classes (instead of creating a bunch of overloads for structs).

  • Share/Bookmark