HiveBrain v1.2.0
Get Started
← Back to all entries
patterncsharpMinor

Unity Lazy Resolve Implementation

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
lazyresolveunityimplementation

Problem

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 where TInterface : class
{
    TInterface Value { get; }
}


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

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

public interface ILazyContract : ILazy, 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 _lazyTypes = new Dictionary();
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(string name = null)
where TLazy : ILazy, TInterface
where TInterface : class
{
Type result;
var @interface = typeof (TInterface);
var @container = typeof (IUni

Solution

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).

Code Snippets

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

        //creation and whatever
    }
}

Context

StackExchange Code Review Q#113343, answer score: 3

Revisions (0)

No revisions yet.