Search code examples
c#expression-trees

Expression Tree Cast to ICollection<T> & Add


I need to create an Action that will very efficiently and repeatedly:

  1. Cast an object-type variable to an ICollection-type variable
  2. Cast an object-type variable to a T-type variable
  3. Add the T-type item to the ICollection-type collection.

It's my understanding that building an expression tree and storing the action for reuse is the fastest way to do this. I'm having a lot of trouble with that. To make this a little more clear, I need an expression-tree-compiled-action that will do this:

private void AddToCollection(Type itemType, object item, object collection)
{
    // assume itemType is used in the expression-tree to cast to ICollection<T>
    ((ICollection<T>)collection).Add((T)item);
}

Solution

  • It's not possible to create efficiently non reflection code which does

    private void AddToCollection(Type itemType, object item, object collection)
    {
        // Assume that itemType became T somehow
        ((ICollection<T>)collection).Add((T)item);
    }
    

    because in order to avoid the reflection, the Type has to be known in advance (either concrete or generic type argument).

    What is possible though is to create something like this:

    static Action<object, object> CreateAddToCollectionAction(Type itemType)
    {
        // Assume that itemType became T somehow
        return (item, collection) => ((ICollection<T>)collection).Add((T)item);
    }
    

    Here is how:

    static Action<object, object> CreateAddToCollectionAction(Type itemType)
    {
        var item = Expression.Parameter(typeof(object), "item");
        var collection = Expression.Parameter(typeof(object), "collection");
        var body = Expression.Call(
            Expression.Convert(collection, typeof(ICollection<>).MakeGenericType(itemType)),
            "Add",
            Type.EmptyTypes,
            Expression.Convert(item, itemType)
        );
        var lambda = Expression.Lambda<Action<object, object>>(body, item, collection);
        return lambda.Compile();
    }
    

    Sample usage:

    var add = CreateAddToCollectionAction(typeof(int));
    object items = new List<int>();
    add(1, items);
    add(2, items);