Skip to content
Immutable collections and helper structures designed for performance and simplicity for .NET
Branch: master
Clone or download
Maksim Volkau
Latest commit 4deb6cd Mar 19, 2019
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
.nuget
BuildScripts added CI scripts and cleanup Mar 19, 2019
nuspecs
playground added CI scripts and cleanup Mar 19, 2019
src converted ImTools to the new sdk based project system based on DryIoc… Mar 10, 2019
test
.editorconfig
.gitattributes
.gitignore
.travis.yml added CI scripts and cleanup Mar 19, 2019
Directory.Build.props converted ImTools to the new sdk based project system based on DryIoc… Mar 10, 2019
ImTools.sln
ImTools.snk converted ImTools to the new sdk based project system based on DryIoc… Mar 10, 2019
ImTools.v3.ncrunchsolution
LICENSE.txt added: NuSpecs Mar 3, 2017
README.md added: appveyor build badge Mar 19, 2019
appveyor.yml
build.bat
build.sh added CI scripts and cleanup Mar 19, 2019

README.md

ImTools

Build status license

  • Lib package: NuGet Badge
  • Code package: NuGet Badge

Immutable persistent collections, Ref, and Array helpers designed for performance.

Note: concurrent HashMap from tests below is out until #2 is fixed.

Split from DryIoc.

Benchmarks

Based on this great benchmark with F# and C# collections.

Lookup

Speed

Lookup Speed

Memory

No GC count

Insert

Speed

Insert Speed

GC count

GC Counts

Remove

Speed

Insert Speed

GC count

GC Counts

Usage Example

Let's assume you are implementing yet another DI container because why not :-)

Container should contain registry of Type to Factory mappings. On resolution Factory is compiled to the delegate which you would like to cache, because compilation is costly. The cache will store the mappings from Type to Func<object>.

The requirements:

  • The container may be used in parallel from different threads including registrations and resolutions.
  • The container state should not be corrupted and the cache should correspond to the current state of registrations.

Let's design the basic container structure to support the requirements and without locking:

    public class Container
    {
        private readonly Ref<Registry> _registry = Ref.Of(new Registry());

        public void Register<TService, TImpl>() where TImpl : TService, new()
        {
            _registry.Swap(reg => reg.With(typeof(TService), new Factory(typeof(TImpl))));
        }

        public object Resolve<TService>()
        {
            return (TService)(_registry.Value.Resolve(typeof(TService)) ?? ThrowUnableToResolve(typeof(TService)));
        }
        
        public object ThrowUnableToResolve(Type t) { throw new InvalidOperationException("Unable to resolve: " + t); }

        class Registry 
        {
            ImHashMap<Type, Factory> _registrations = ImHashMap<Type, Factory>.Empty;
            Ref<ImHashMap<Type, Func<object>>> _resolutionCache = Ref.Of(ImHashMap<Type, Func<object>>.Empty);

            // Creating a new registry with +1 registration and new refeence to cache value
            public Registry With(Type serviceType, Factory implFactory)
            {
                return new Registry() 
                {	
                    _registrations = _registrations.AddOrUpdate(serviceType, implFactory),
                        
                    // Here is most interesting part:
                    // We are creating new independent reference pointing to cache value,
                    // isolating it from possible parallel resolutions, 
                    // which will swap older version/ref of cache and won't touch the new one.
                    _resolutionCache = Ref.Of(_resolutionCache.Value)
                };
            }

            public object Resolve(Type serviceType)
            {
                var func = _resolutionCache.Value.GetValueOrDefault(serviceType);
                if (func != null)
                    return func();

                var reg = _registrations.GetValueOrDefault(serviceType);
                if (reg == null)
                    return null;
                
                func = reg.CompileDelegate();
                _resolutionCache.Swap(cache => cache.AddOrUpdate(serviceType, func));
                return func.Invoke();
            }
        }
        
        class Factory 
        {
            public readonly Type ImplType;
            public Factory(Type implType) { ImplType = implType; }
            public Func<object> CompileDelegate() { return () => Activator.CreateInstance(ImplType); }
        } 
    }
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.