3
\$\begingroup\$

I have been using Microsoft.Unity as my container and have decided that the approach for a lazy implementation causes too much rework. Each time you decide to swap out to use a lazy you have to go add .Value or a .Resolve() after your variable (which could be called hundreds of times).

Suggested lazy implementation by microsoft Deferring the Resolution of Objects

So I decided to start from scratch.

Firstly I needed an ILazy interface (I did add the Value property so you can get the actual value if it is ever needed)

public interface ILazy<out TInterface> where TInterface : class
{
    TInterface Value { get; }
}

We then need an interface and a type that is both ILazy<T> and of the interface to be implemented.

public interface IContract
{
    //methods and properties to be implemented
    void SomeFunction();
}

public interface ILazyContract : ILazy<IContract>, IContract
{

}

public class ContractImplementation : IContract
{
    public void SomeFunction()
    {
        Console.WriteLine("Doing something");
    }
}

I now need a factory which creates the implementation of the ILazyContract at runtime. This is a long class and has a bit of il generation so it might be difficult to read.

public class LazyTypeFactory
{
    //used to stop the same class being created twice
    private readonly object _lock = new object();
    private readonly Dictionary<Type, Type> _lazyTypes = new Dictionary<Type, Type>();
    private readonly ModuleBuilder _moduleBuilder;

    public LazyTypeFactory(string assembly, string module)
    {
        var an = new AssemblyName(assembly);
        var assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(an, AssemblyBuilderAccess.Run);
        _moduleBuilder = assemblyBuilder.DefineDynamicModule(module);
    }

    public Type CreateOrGetLazyType<TLazy, TInterface>(string name = null)
        where TLazy : ILazy<TInterface>, TInterface
        where TInterface : class
    {
        Type result;
        var @interface = typeof (TInterface);
        var @container = typeof (IUnityContainer);
        var @lazy = typeof (TLazy);
        var @func = typeof (Func<IUnityContainer, TInterface>);

        name = name ?? @lazy.Name + "Impl";
        name = @lazy.Namespace + "." + name;

        if (!@interface.IsInterface)
        {
            throw new Exception("Expected TInterface to be a type of interface");
        }

        lock (_lock)
        {
            if (_lazyTypes.ContainsKey(@lazy))
            {
                result = _lazyTypes[@lazy];
            }
            else
            {
                var typeBuilder = _moduleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Class, null);
                typeBuilder.AddInterfaceImplementation(@lazy);

                //private field for container
                var c = typeBuilder.DefineField("_c", @container, FieldAttributes.Public);
                //private field for func
                var f = typeBuilder.DefineField("_f", @func, FieldAttributes.Public);
                //private field for func result
                var v = typeBuilder.DefineField("_v", @interface, FieldAttributes.Public);

                //constructor with func
                CreateConstructor(typeBuilder, @container, c, @func, f);

                //private property to get func result
                var propertyBuilder = CreateValueProperty(typeBuilder, @interface, v, @container, c, @func, f);

                //interface methods
                //call prop then invok method
                foreach (var iMethod in @interface.GetMethods())
                {
                    var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);

                    typeBuilder.DefineMethodOverride(mb, iMethod);
                }

                foreach (var iProperty in @interface.GetProperties())
                {
                    var pb = typeBuilder.DefineProperty(iProperty.Name, PropertyAttributes.None, iProperty.PropertyType, Type.EmptyTypes);

                    if (iProperty.GetMethod != null)
                    {
                        var iMethod = iProperty.GetMethod;
                        var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);

                        pb.SetGetMethod(mb);
                    }

                    if (iProperty.SetMethod != null)
                    {
                        var iMethod = iProperty.SetMethod;
                        var mb = CreateOverride(typeBuilder, propertyBuilder, iMethod);

                        pb.SetSetMethod(mb);
                    }
                }

                _lazyTypes[@lazy] = result = typeBuilder.CreateType();
            }
        }

        return result;
    }

    private static void CreateConstructor(TypeBuilder typeBuilder, Type @container, FieldBuilder c, Type @func, FieldBuilder f)
    {
        var constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard, new[] { @container, @func });

        constructor.DefineParameter(1, ParameterAttributes.None, "container");
        constructor.DefineParameter(2, ParameterAttributes.None, "func");

        var cIl = constructor.GetILGenerator();

        cIl.Emit(OpCodes.Ldarg_0);
        cIl.Emit(OpCodes.Ldarg_1);
        cIl.Emit(OpCodes.Stfld, c);

        cIl.Emit(OpCodes.Ldarg_0);
        cIl.Emit(OpCodes.Ldarg_2);
        cIl.Emit(OpCodes.Stfld, f);

        cIl.Emit(OpCodes.Ret);
    }

    private static PropertyBuilder CreateValueProperty(TypeBuilder typeBuilder, Type @interface, FieldBuilder v, Type @container, FieldBuilder c, Type @func, FieldBuilder f)
    {
        var propertyBuilder = typeBuilder.DefineProperty("Value", PropertyAttributes.None, @interface, Type.EmptyTypes);
        var get = typeBuilder.DefineMethod("get_Value", MethodAttributes.Public | MethodAttributes.Virtual, @interface, new Type[0]);
        var getIl = get.GetILGenerator();

        var skip = getIl.DefineLabel();
        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldfld, v);
        getIl.Emit(OpCodes.Ldnull);
        getIl.Emit(OpCodes.Bne_Un, skip);

        //call _f and set to _v
        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldfld, f);
        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldfld, c);
        getIl.Emit(OpCodes.Call, @func.GetMethod("Invoke", new[] { @container }));
        getIl.Emit(OpCodes.Stfld, v);

        getIl.MarkLabel(skip);

        getIl.Emit(OpCodes.Ldarg_0);
        getIl.Emit(OpCodes.Ldfld, v);
        getIl.Emit(OpCodes.Ret);

        propertyBuilder.SetGetMethod(get);

        return propertyBuilder;
    }

    private static MethodBuilder CreateOverride(TypeBuilder typeBuilder, PropertyBuilder propertyBuilder, MethodInfo iMethod)
    {
        var mb = typeBuilder.DefineMethod(iMethod.Name, MethodAttributes.Public | MethodAttributes.Virtual,
            iMethod.ReturnType, iMethod.GetParameters().Select(x => x.ParameterType).ToArray());
        var mIL = mb.GetILGenerator();

        mIL.Emit(OpCodes.Ldarg_0);
        mIL.Emit(OpCodes.Call, propertyBuilder.GetMethod);

        for (var i = 0; i < iMethod.GetParameters().Length; i++)
        {
            mIL.Emit(OpCodes.Ldarg, i + 1);
        }

        mIL.Emit(OpCodes.Call, iMethod);
        mIL.Emit(OpCodes.Ret);

        return mb;
    }
}

To create a Type of ILazyContract at runtime you would do something like this.

var factory = new LazyTypeFactory("Assembly.Lazy", "Module.Lazy");
var type = factory.CreateOrGetLazyType<ILazyContract, IContract>();

The implementation of this would look something like

public class ILazyContractImpl : ILazyContract
{
    private readonly IUnityContainer _container;
    private readonly Func<IUnityContainer, IContract> _func;
    private IContract _value;

    public ILazyContractImpl(IUnityContainer container, Func<IUnityContainer, IContract> func)
    {
        _container = container;
        _func = func;
    }

    public IContract Value
    {
        get
        {
            if (_value == null)
            {
                _value = _func(_container);
            }

            return _value;
        }
    }

    public void SomeFunction()
    {
        Value.SomeFunction();
    }
}

As for the final part of the puzzle you now have to register this in the unity container. For this we also need to either register a Func<IUnityContainer, IContract> or add it as a parameter in a InjectionConstructor which is what I have done.

IUnityContainer container = new UnityContainer();
var factory = new LazyTypeFactory("Assembly.Lazy", "Module.Lazy");
var type = factory.CreateOrGetLazyType<ILazyContract, IContract>();

container.RegisterType<IContract, ContractImplementation>();

var resolve = new Func<IUnityContainer, IContract>(c => c.Resolve<IContract>());
container.RegisterType(typeof(ILazyContract), type, new InjectionConstructor(typeof(IUnityContainer), resolve));

var lazy = container.Resolve<ILazyContract>();
lazy.SomeFunction();

So there is my take on lazy - which wasn't very lazy :(.

Any feedback would be greatly appreciated, also if there are any improvements that can be made please let me know.


As requested by Heslacher, this is a real implementation where I wanted to cache the UserSettings because they will not change over the life span of a request.

Here you can see that the constructor will make a call to the db, which could take some time. So instead I would like to only do this when the class is actually used, not when it is created.

public interface IUserConfigurationService
{
    UserSetting[] GetSettings();
    UserSetting[] GetSettingsForProduct(Product product);
    UserSetting[] GetSettingsOfType(SettingType type);
}

public class UserConfigurationService : IUserConfigurationService
{
    private readonly Entities _entities;
    private readonly User _currentUser;
    //a cache so that we do not have to make the same call many times
    private readonly UserSetting[] _userSettings;

    public UserConfigurationService(Entities entities, User currentUser)
    {
        _entities = entities;
        _currentUser = currentUser;

        _userSettings = Entities.UserSettings.Where(x => x.User == currentUser).ToArray();
    }

    public UserSetting[] GetSettings()
    {
        return _userSettings;
    }

    public UserSetting[] GetSettingsForProduct(Product product)
    {
        return _userSettings.Where(x => x.Product == product).ToArray();
    }

    public UserSetting[] GetSettingsOfType(SettingType type)
    {
        return _userSettings.Where(x => x.SettingType == type).ToArray();
    }
}
\$\endgroup\$
3
\$\begingroup\$

I think you might have a performance problem with CreateOrGetLazyType. Obviously, I'm not talking about the IL generation, we know that's not particularly fast. I'm talking about the lock statement.

While I'm building a type, no one can grab a lazy type from the dictionary. That might become as problem as the IL generation is, as said above, kinda slow.

I'd change :

lock (_lock)
{
    if (_lazyTypes.ContainsKey(@lazy))
    {
        result = _lazyTypes[@lazy];
    }
    else
    {
        //creation and whatever
    }
}

to :

if (_lazyTypes.ContainsKey(@lazy))
{
    result = _lazyTypes[@lazy];
}
else
{
    lock (_lock)
    {
        if (_lazyTypes.ContainsKey(@lazy))
        {
            return _lazyTypes[@lazy];
        }

        //creation and whatever
    }
}

Now yeah, duplicated code, I know! But sit awhile and listen. :p What's the advantage of this over your current solution? Your first scenario, the one where the dictionary contains the key, can be executed without caring about the lock. And that's good! You don't want one IL generation to block several calls to the dictionary!

Of course, in my case we need to re-check inside the lock if the key was added meanwhile. If so, we return it.

Also, nesting 101 : Nesting is bad. Avoid it when you can. In your case, you can afford to remove a else statement. Instead of setting your result variable and returning it at the end of your method, you should return right away. This way you can remove the else. (That's what I did in my example).

\$\endgroup\$
  • \$\begingroup\$ Is it safe to do the ContainsKey or TryGetValue in a multi-threaded environment? Or should I use a ConcurrentDictionary. It was safe to do so earlier as it was always locked. \$\endgroup\$ – sQuir3l Dec 9 '15 at 14:51
  • \$\begingroup\$ @sQuir3l In that scenario I think it's safe. But I'm not enough of a concurrence expert :p I feel like the ConcurrentDictionary would have an overhead considering your scenario (where you lazy load stuff) \$\endgroup\$ – IEatBagels Dec 9 '15 at 14:52
  • \$\begingroup\$ I did a quick read and I think that I do need to use the ConcurrentDictionary shown stackoverflow.com/questions/4000347/… and social.msdn.microsoft.com/Forums/vstudio/en-US/… . So maybe you want to update your answer :) \$\endgroup\$ – sQuir3l Dec 9 '15 at 14:57
2
\$\begingroup\$

CreateOrGetLazyType<TLazy, TInterface>

This

var @container = typeof (IUnityContainer);
var @lazy = typeof (TLazy);
var @func = typeof (Func<IUnityContainer, TInterface>);

name = name ?? @lazy.Name + "Impl";
name = @lazy.Namespace + "." + name;  

won't be needed if (!@interface.IsInterface) so better do this check earlier.

Furthermore this

name = name ?? @lazy.Name + "Impl";
name = @lazy.Namespace + "." + name;   

can simplified like so

name = @lazy.Namespace + "." + (name ?? @lazy.Name + "Impl");  

Using ContainsKey() together with the getter of the Item property of a Dictionary<TKey, TValue> should be replaced by a call to TryGetValue() which is faster because the check if the key exists is done by the Item getter too.
Internally these three methods are calling the FindEntry() method to check wether a given key exists. So calling this method only once through the TryGetValue() method should be the way to go.


If the key is found in the dictionary you should return early to save horizontal spacing.


Comments like this

//constructor with func
CreateConstructor(typeBuilder, @container, c, @func, f);  

don't add any value to the code. You should remove such noise to make your code more readable.


All this single/double letter variable names makes it impossible to grasp the sense of them at first glance. You or Sam the maintainer will have a hard time by looking at this code to fix a bug or just to refactor it.

Variable names should be as descriptive as possible.


The method name CreateOrGetLazyType implies that it will first try to create and if that doesn't work it will get a LazyType. A much clearer name would be GetOrCreateLazyType.

\$\endgroup\$
  • \$\begingroup\$ Ty, all of that makes good sense. Will implement in a bit. \$\endgroup\$ – sQuir3l Dec 9 '15 at 11:36
0
\$\begingroup\$

While doing some rework suggested by Heslacher, I saw an opportunity to clean up some of the intermediate language generation.
It is much easier to read and debug a class that is not generated at runtime.

So I added an implementation for ILazy<T> and extended this class when creating the lazy implementation of an interface.

public class Lazy<TInterface> : ILazy<TInterface> 
    where TInterface : class
{
    private readonly object _lock = new object();
    private bool _isSet = false;

    private readonly IUnityContainer _container;
    private readonly Func<IUnityContainer, TInterface> _func;
    private TInterface _value;

    public Lazy(IUnityContainer container, Func<IUnityContainer, TInterface> func)
    {
        _container = container;
        _func = func;
    }

    public TInterface Value
    {
        get
        {
            //quick check so that lock is not called
            if (_isSet)
            {
                return _value;
            }

            lock (_lock)
            {
                //double check for certainty
                if (_isSet)
                {
                    return _value;
                }

                _value = _func(_container);
                _isSet = true;
            }

            return _value;
        }
    }
}

I then set it as the base class when I create my TypeBuilder

var @lazyInterface = typeof(Lazy<TInterface>);
var typeBuilder = _moduleBuilder.DefineType(name, TypeAttributes.Public | TypeAttributes.Class, @lazyInterface);

This clears up all the private FieldBuilders and the PropertyBuilder. This therefore also cleans up much of the il generation.

This did mean that I have to update my CreateConstructor method as such.
But this turned out to be better as well imo.

    private static void CreateConstructor(TypeBuilder typeBuilder, Type @container, Type @func, ConstructorInfo baseConstructor)
    {
        var constructor = typeBuilder.DefineConstructor(MethodAttributes.Public, CallingConventions.Standard | CallingConventions.HasThis, new[] { @container, @func });

        constructor.DefineParameter(1, ParameterAttributes.None, "container");
        constructor.DefineParameter(2, ParameterAttributes.None, "func");

        var cIl = constructor.GetILGenerator();

        cIl.Emit(OpCodes.Ldarg_0);
        cIl.Emit(OpCodes.Ldarg_1);
        cIl.Emit(OpCodes.Ldarg_2);

        cIl.Emit(OpCodes.Call, baseConstructor);

        cIl.Emit(OpCodes.Ret);
    }
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.