Write a lock free singleton factory using generics in c# I am writing a generic CacheProvider factory which contains a concurrent dictionary to keep the "Named" cache objects (a custom implementation of memory cache based on concurrent dictionary) to be shared among different state machines/pipelines.

I found this article and came up with below implementation 

public class CacheProvider&lt;TKey, TType&gt;
{
    private Eventing.Tracer tracer;
    public delegate TType CreateObjectDelegate();
    private readonly ConcurrentDictionary&lt;TKey, CreateObjectDelegate&gt; FactoryMap;
    private ConcurrentDictionary&lt;TKey, TType&gt; cachePool;
    private static object _lock = new object();

    /// &lt;summary&gt;
    /// This class is a singleton wrapper on custom memory cache class to be shared with different state machines
    /// &lt;/summary&gt;
    /// &lt;param name="tracer"&gt;&lt;/param&gt;
    public CacheProvider(Eventing.Tracer tracer)
    {
        Contract.Requires&lt;ArgumentException&gt;(tracer != null, "tracer cannot be null");
        this.tracer = tracer;
        cachePool = new ConcurrentDictionary&lt;TKey, TType&gt;();
        FactoryMap = new ConcurrentDictionary&lt;TKey, CreateObjectDelegate&gt;();
    }

    public void Register(TKey key, CreateObjectDelegate ctor)
    {
     // This method registers a delegate to the method used to create the 
       object so that it can be created later.
       //
      FactoryMap.TryAdd(key, ctor);
    }

    public TType CreateOrGetMemoryCache(TKey key)
    {
            if (cachePool.TryGetValue(key, out TType memoryCache))
            {
                tracer.Trace(SplitterTraceCodes.CacheProvider, $"MemoryCache with cache key: {key} already created.");
                return memoryCache;
            }
            else
            {
                tracer.Trace(SplitterTraceCodes.CacheProvider, $"MemoryCache with cache key: {key} not found.");

                // Create a new one
                CreateObjectDelegate constructor = null;
                if (FactoryMap.TryGetValue(key, out constructor))
                {
                    cachePool.TryAdd(key,constructor());
                    cachePool.TryGetValue(key, out TType memoryCache);
                    return memoryCache;
                }

                throw new ArgumentException("Error in Creating MemoryCache. No Type registered for this Cache Key");
            }
    }
}


TType is as follows:

MyBase 
{
  string BaseProperty1;
} 
MyChild1 : MyBase
{
  string ChildProperty1;
}
MyChild2 : MyBase
{
 string ChildProperty2;
}


I am looking to setup this at service startup as follows:

CacheProvider&lt;string, object&gt; cacheProvider= new CacheProvider&lt;string, object&gt;();
CacheProvider&lt;string, object&gt;.CreateObjectDelegate createCacheForMyChild1 = new CacheProvider&lt;string, object&gt;.CreateObjectDelegate(CustomConcurrentCache&lt;string,MyChild1&gt;());
CacheProvider&lt;string, object&gt;.CreateObjectDelegate createCacheForMyChild2 = new CacheProvider&lt;string, object&gt;.CreateObjectDelegate(CustomConcurrentCache&lt;string,MyChild2&gt;());
cacheProvider.Register("MyChild1Cache", createCacheForMyChild1 );
cacheProvider.Register("MyChild2Cache", createCacheForMyChild2 );


and use it wherever I need the named cache instance:

CustomConcurrentCache&lt;string,MyChild1&gt; cache =  (MyChild1)cacheProvider.CreateOrGetMemoryCache&lt;object&gt;("MyChild1Cache");


Can this be done without using the delegates? Is there any other better way to achieve this?
A delegate can be seen as method signature contract. You can define what sort of methods are you willing to work with. For example


The method should accept type x, type y and type z as parameters
and should return with type w


Any method, which satisfy this contract, is a valid method from the delegate perspective.  



public delegate TType CreateObjectDelegate();


This delegate says that any method that has no parameter and will return with TType is a good one.

You can describe the same by using the Func class:

Func&lt;TType&gt; CreateObjectDelegate


Whenever you want to call the Register method you just have to pass a method which satisfies this contract. For example (let's suppose TType is int):

cacheProvider.Register("MyChild1Cache", () =&gt; 0);

Func&lt;int&gt; alternative1 = () =&gt; 1;
cacheProvider.Register("MyChild2Cache", alternative1.Invoke);

Func&lt;int, Func&lt;int&gt;&gt; alternative2 = i =&gt; () =&gt; i;
cacheProvider.Register("MyChild3Cache", alternative2.Invoke(2).Invoke);