This project is read-only.

Type overload of RegisterInitializer

Apr 28, 2013 at 7:23 AM
I'm implementing IContainer from NServiceBus.
http://support.nservicebus.com/customer/portal/articles/852357-containers

void ConfigureProperty(Type component, string property, object value);

looks like a perfect fit for:
public void RegisterInitializer<TService>(Action<TService> instanceInitializer)

Except for the type to generic issue.

Looking at the implementation, it looks like it ends up as a Type anyway:
        this.instanceInitializers.Add(new InstanceInitializer
        {
            ServiceType = typeof(TService),
            Action = instanceInitializer,
        });
Would you be willing to add or take a pull request for a Type overload of RegisterInitializer?
public void RegisterInitializer(Type serviceType, Action<object> instanceInitializer)
Apr 28, 2013 at 4:51 PM
I tried playing with reflection to get this done. So far it appears to be working. See anything horrible?
public void ConfigureProperty(Type component, string property, object value)
{
    if (value == null)
    {
        return;
    }

    var method = typeof (Container).GetMethod("RegisterInitializer").MakeGenericMethod(component);
    Action<object> action = o => SetPropertyValue(o, property, value);
    method.Invoke(container, new object[] {action});
}

///<summary>
/// Set a property value on an instance using reflection
///</summary>
///<param name="instance"></param>
///<param name="propertyName"></param>
///<param name="value"></param>
static void SetPropertyValue(object instance, string propertyName, object value)
{
    instance.GetType()
            .GetProperty(propertyName, BindingFlags.Public | BindingFlags.Instance)
            .SetValue(instance, value, null);
}
Apr 28, 2013 at 5:13 PM
When a user would call a RegisterInitializer(Type, Action<object>) method, this would almost always mean that the registered Action<object> needs to do some reflection to do its job, as is the case in your situation. Letting users do something that is that slow by nature is something I like to prevent with Simple Injector. Everything must be fast. Besides this, its usefulness is very limited, and there is a quite good workaround available (see below).

Because of this I don't feel this feature fits the Simple Injector strategy and I will not accept a pull request on this.

The code you posted looks fine, but is slow. So consider the following alternative instead:
public static void ConfigureProperty(this Container container,
    Type component, string property, object value)
{
    var prop = component.GetProperty(property);

    if (value == null) throw new ArgumentNullException("value");
    if (prop == null) throw new ArgumentException("property '" + property + "' not found");
    if (!prop.PropertyType.IsAssignableFrom(value.GetType()))
        throw new ArgumentException("value type is incorrect", "value");

    var actionType = typeof(Action<>).MakeGenericType(component);

    var parameter = Expression.Parameter(component);

    var action = Expression.Lambda(actionType,
        Expression.Assign(
            Expression.Property(parameter, property),
            Expression.Constant(value)),
        parameter)
        .Compile();

    var initializer = typeof(Container).GetMethod("RegisterInitializer")
        .MakeGenericMethod(component);

    initializer.Invoke(container, new [] { action });
}
This extension method builds an Action<T> delegate that assigns the value to the class property. There's a lot of reflection going on, but it is one time. The delegate is compiled and registered. Resolving an instance of the 'component' type will be very fast and will not use any reflection.

I hope this helps.
Marked as answer by dot_NET_Junkie on 11/5/2013 at 7:40 AM
Apr 28, 2013 at 6:02 PM
Awesome!

R# is suggesting changing
if (!prop.PropertyType.IsAssignableFrom(value.GetType()))
to
if (!prop.PropertyType.IsInstanceOfType(value.GetType()))
Ignore?
Apr 28, 2013 at 7:56 PM
The suggestion is wrong, but this would be correct:
if (!prop.PropertyType.IsInstanceOfType(value))
Apr 28, 2013 at 8:32 PM
Oh, what you wrote is the actual R# suggestion. I missed that bit of nuance and copy pasted incorrectly.

Thanks.