{"id":19991579,"url":"https://github.com/neuecc/MicroResolver","last_synced_at":"2025-05-04T10:32:05.012Z","repository":{"id":66289654,"uuid":"96226456","full_name":"neuecc/MicroResolver","owner":"neuecc","description":"Extremely Fast Dependency Injection Library.","archived":true,"fork":false,"pushed_at":"2017-12-21T11:59:30.000Z","size":63,"stargazers_count":182,"open_issues_count":0,"forks_count":18,"subscribers_count":12,"default_branch":"master","last_synced_at":"2025-05-02T06:46:59.837Z","etag":null,"topics":["csharp","dependency-injection","ioc-container","service-locator"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/neuecc.png","metadata":{"files":{"readme":"ReadMe.md","changelog":null,"contributing":null,"funding":null,"license":"license","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2017-07-04T14:27:50.000Z","updated_at":"2025-04-29T03:35:04.000Z","dependencies_parsed_at":"2023-06-03T03:30:27.416Z","dependency_job_id":null,"html_url":"https://github.com/neuecc/MicroResolver","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neuecc%2FMicroResolver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neuecc%2FMicroResolver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neuecc%2FMicroResolver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/neuecc%2FMicroResolver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/neuecc","download_url":"https://codeload.github.com/neuecc/MicroResolver/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252320505,"owners_count":21729139,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["csharp","dependency-injection","ioc-container","service-locator"],"created_at":"2024-11-13T04:51:49.122Z","updated_at":"2025-05-04T10:32:04.586Z","avatar_url":"https://github.com/neuecc.png","language":"C#","funding_links":[],"categories":["C\\#"],"sub_categories":[],"readme":"MicroResolver\n===\nExtremely Fast Dependency Injection Library.\n\nFeatures\n---\nMicroResolver is desgined for performance. I've released two fastest serializers [ZeroFormatter](https://github.com/neuecc/ZeroFormatter) and [MessagePack for C#](https://github.com/neuecc/MessagePack-CSharp), this library is using there dynamic il code generation technique.\n\n* [Dynamic IL Inlining](https://github.com/neuecc/MicroResolver#performance-technique---dynamic-il-inlining)\n* [Generic Type Caching per resolver](https://github.com/neuecc/MicroResolver#performance-technique---generic-type-caching-per-resolver)\n* [Fast NonGeneric lookup table](https://github.com/neuecc/MicroResolver#performance-technique---fast-nongeneric-lookup-table)\n\nMicroResolver achived fastest at some tests in [IoCPerformance](https://github.com/danielpalme/IocPerformance) benchmark -  Singleton(Multithread), Combined(Multithread), Property(Singlethread, Multithread) and other result also top level. This benchmark is nongeneric test(use (`object Resove(Type type)`), MicroResolver is focused to optimize for generic method(`T Resolve\u003cT\u003e()`), if using it, faster than other libraries.\n\nSupport Features - Consturctor Injection, Field Injection, Property Injection, Method Injection, Collection resolver and Three lifetime support(Singleton, Transient and Scoped).\n\nQuick Start\n---\nInstall from NuGet(for .NET Framework 4.6, .NET Standard 1.4)\n\n* Install-Package [MicroResolver](https://www.nuget.org/packages/MicroResolver)\n\n```csharp\n// Create a new container\nvar resolver = ObjectResolver.Create();\n\n// Register interface-\u003etype map, default is transient(instantiate every request)\nresolver.Register\u003cIUserRepository, SqlUserRepository\u003e();\n\n// You can configure lifestyle - Transient, Singleton or Scoped\nresolver.Register\u003cILogger, MailLogger\u003e(Lifestyle.Singleton);\n\n// Compile and Verify container(this is required step)\nresolver.Compile();\n\n// Get instance from container\nvar userRepository = resolver.Resolve\u003cIUserRepository\u003e();\nvar logger = resolver.Resolve\u003cILogger\u003e();\n```\n\nNotice: MicroResolver requests call `Compile` before use container.\n\nInjectionAttribute and Resolve Collection\n---\nMicroResolver can resolve all public and private properties, fields, constructor and methods. Inject target have to mark `[Inject]` attribute.\n\n```csharp\npublic class MyType : IMyType\n{\n    // field injection\n\n    [Inject]\n    public IInjectTarget PublicField;\n\n    [Inject]\n    IInjectTarget PrivateField;\n\n    // property injection\n\n    [Inject]\n    public IInjectTarget PublicProperty { get; set; }\n\n    [Inject]\n    IInjectTarget PrivateProperty { get; set; }\n\n    // constructor injection\n    // if not marked [Inject], the constructor with the most parameters is used.\n    [Inject]\n    public MyType(IInjectTarget x, IInjectTarget y, IInjectTarget z)\n    {\n\n    }\n\n    // method injection\n\n    [Inject]\n    public void Initialize1()\n    {\n    }\n\n    [Inject]\n    public void Initialize2()\n    {\n    }\n}\n\n// and resolve it\nvar v = resolver.Resolve\u003cIMyType\u003e();\n```\n\nInject order is `Constructor -\u003e Field -\u003e Property -\u003e Method`.\n\nIf register many types per type, you can use `RegisterCollection` and `Resolve\u003cIEnumerable\u003cT\u003e\u003e`.\n\n```csharp\n// Register type -\u003e many types\nresolver.RegisterCollection\u003cIMyType\u003e(typeof(T1), typeof(T2), typeof(T3));\n\nresolver.Compile();\n\n// can resolve by IEnumerbale\u003cT\u003e or T[] or IReadOnlyList\u003cT\u003e.\nresolver.Resolve\u003cIEnumerable\u003cIMyType\u003e\u003e();\nresolver.Resolve\u003cIMyType[]\u003e();\nresolver.Resolve\u003cIReadOnlyList\u003cIMyType\u003e\u003e();\n\n// can resolve other type's inject target.\npublic class AnotherType\n{\n    public AnotherType(IMyType[] targets)\n    {\n    }\n}\n```\n\nScoped\n---\n`Lifetime.Scoped` is usually the same as Transient but within BeginScope it behaves like a singleton in the scope.\n\n```csharp\n// sample type of check scope\npublic class MyClass : IMyType, IDisposable\n{\n    public MyClass()\n    {\n        Console.WriteLine(\"Created\");\n    }\n\n    public void Dispose()\n    {\n        Console.WriteLine(\"Disposed\");\n    }\n}\n\n// -----------\n\nvar resolver = ObjectResolver.Create();\nresolver.Register\u003cIMyType, MyClass\u003e(Lifestyle.Scoped);\nresolver.Compile();\n\nusing (var coResolver = resolver.BeginScope(ScopeProvider.Standard))\n{\n    var i1 = coResolver.Resolve\u003cIMyType\u003e(); // \"Created\"\n    var i2 = coResolver.Resolve\u003cIMyType\u003e();\n\n    Console.WriteLine(Object.ReferenceEquals(i1, i2)); // \"True\" -\u003e same instance\n\n    // if scope end and instantiated types is IDisposable, called Dispose.\n} // \"Disposed\"\n```\n\n`ScopeProvider` has three option in default. ` ScopeProvider.Standard`, `ScopeProvider.ThreadLocal` and `ScopeProvider.AsyncLocal`. If needs custom scope, you can create own ScopeProvider.\n\n```csharp\npublic class MyScopeProvider : ScopeProvider\n{\n    public override void Initialize(IObjectResolver resolver)\n    {\n        // when called from BeginScope().\n    }\n\n    protected override object GetValueFromScoped(Type type, out bool isFirstCreated)\n    {\n        // called per Resolve\u003cT\u003e.\n    }\n}\n```\n\nPerformance Technique - Dynamic IL Inlining\n---\nEveryone creates dynamic code generation for optimize performance. But if target is complex type?\n\n```csharp\n// sample of complex dependency type\npublic class ForPropertyInjection : IForPropertyInjection\n{\n    [Inject]\n    public void OnCreate()\n    {\n    }\n}\n\npublic class ForConstructorInjection : IForConsturctorInjection\n{\n    [Inject]\n    public IForFieldInjection MyField;\n}\n\npublic class ComplexType : IComplexType\n{\n    [Inject]\n    public IForPropertyInjection MyProperty { get; set; }\n\n    public ComplexType(IForConsturctorInjection instance1)\n    {\n\n    }\n\n    [Inject]\n    public void Initialize()\n    {\n    }\n}\n\n// for example, how to resolve ComplexType?\nvar v = resolver.Resolve\u003cIComplexType\u003e();\n```\n\nThe following way is not slow, but it is not fastest.\n\n```csharp\n// This is `slow` example of complex type resolve\nstatic IComplexType ResolveComplexType(IObjectResolver resolver)\n{\n    var a = resolver.Resolve\u003cIForConsturctorInjection\u003e();\n    var b = resolver.Resolve\u003cIForPropertyInjection\u003e();\n\n    var result = new ComplexType(a);\n    result.MyProperty = b;\n    result.Initialize();\n\n    return result;\n}\n```\n\nMicroResolve choose inlining code generation, all dependencies are analyzed and inlined at compile time.\n\n```csharp\n// This is actual code generation of MicroResolver, all dependency is inlined at il code generation\nstatic IComplexType ResolveComposite()\n{\n    var a = new ForConstructorInjection();\n    a.MyField = new ForFieldInjection();\n    var b = new ForPropertyInjection();\n    b.OnCreate();\n\n    var result = new ComplexType(a);\n    result.MyProperty = b;\n    result.Initialize();\n\n    return result;\n}\n```\n\nPerformance Technique - Generic Type Caching per resolver\n---\nThe generated code is cached. And how to retrieve it? ConcurrentDictionary? Dictionary? They are slow. MicroResolve choose generic type caching.\n\nThis is `ObjectResolver` signature.\n\n```csharp\npublic abstract class ObjectResolver\n{\n    public abstract T Resolve\u003cT\u003e();\n}\n```\n\nIf called `ObjectResolver.Create`, generate dynamic inherited type.\n\n```csharp\npublic class ObjectResolver_Generated : ObjectResolver\n{\n    public override T Resolve\u003cT\u003e()\n    {\n        // too simple, of course simple is fastest.\n        return Cache\u003cT\u003e.factory();\n    }\n\n    Cache\u003cT\u003e\n    {\n        // generated Func\u003cT\u003e code is see 'Dynamic IL Inlining' section.\n        public Func\u003cT\u003e factory;\n    }\n}\n```\n\nThe code path is too short, it means no overhead.\n\n\u003e But generated container can not remove. This is a design constraint.\n\nPerformance Technique - Fast NonGeneric lookup table\n---\nType Caching is require to use generics method. But often framework requests nongeneric type.\n\n```csharp\n// fastest\nresolver.Resolve\u003cT\u003e();\n\n// slower but framework requests this method\n(T)resolver.Resolve(type);\n```\n\nMicroResolver use fast type lookup by own fixed hashtable.\n\n```csharp\n// buckets item\nstruct HashTuple\n{\n    public Type type;\n    public Func\u003cobject\u003e factory;\n}\n\n// simplest hash table(fixed-array chaining hashtable)\nprivate HashTuple[][] table;\n\n// register - Func\u003cT\u003e -\u003e Func\u003cobject\u003e by delegate covariance\ntable[hash][index] = new Func\u003cobject\u003e(Cache\u003cT\u003e.factory);\n\n// simplest == fastest lookup\npublic object Resolve(Type type)\n{\n    var hashCode = type.GetHashCode();\n    var buckets = table[hashCode \u0026 tableMaskIndex]; // table size is power of 2, fast lookup\n\n    // .Length for loop can remove array bounds check\n    for (int i = 0; i \u003c buckets.Length; i++)\n    {\n        if (buckets[i].type == type)\n        {\n            return buckets[i].factory();\n        }\n    }\n\n    throw new MicroResolverException(\"Type was not dound, Type: \" + type.FullName);\n}\n```\n\nnon-generic lookup is slower than generic but still fast.\n\nAuthor Info\n---\nYoshifumi Kawai(a.k.a. neuecc) is a software developer in Japan.  \nHe is the Director/CTO at Grani, Inc.  \nGrani is a mobile game developer company in Japan and well known for using C#.  \nHe is awarding Microsoft MVP for Visual C# since 2011.  \nHe is known as the creator of [UniRx](http://github.com/neuecc/UniRx/)(Reactive Extensions for Unity)  \n\nBlog: [https://medium.com/@neuecc](https://medium.com/@neuecc) (English)  \nBlog: [http://neue.cc/](http://neue.cc/) (Japanese)  \nTwitter: [https://twitter.com/neuecc](https://twitter.com/neuecc) (Japanese)   \n\nLicense\n---\nThis library is under the MIT License.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneuecc%2FMicroResolver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fneuecc%2FMicroResolver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fneuecc%2FMicroResolver/lists"}