diff --git a/README.md b/README.md index 85119f87e..dfcba7762 100644 --- a/README.md +++ b/README.md @@ -199,7 +199,7 @@ dotnet run - [Resolve methods](readme/resolve-methods.md) - [Simplified binding](readme/simplified-binding.md) - [Factory](readme/factory.md) -- [Arguments](readme/arguments.md) +- [Class arguments](readme/class-arguments.md) - [Root arguments](readme/root-arguments.md) - [Tags](readme/tags.md) - [Field injection](readme/field-injection.md) @@ -209,16 +209,16 @@ dotnet run - [Required properties or fields](readme/required-properties-or-fields.md) - [Root binding](readme/root-binding.md) ### Lifetimes +- [Transient](readme/transient.md) - [Singleton](readme/singleton.md) - [PerResolve](readme/perresolve.md) -- [Scope](readme/scope.md) - [PerBlock](readme/perblock.md) -- [Transient](readme/transient.md) -- [Disposable singleton](readme/disposable-singleton.md) -- [Async disposable scope](readme/async-disposable-scope.md) -- [Async disposable singleton](readme/async-disposable-singleton.md) +- [Scope](readme/scope.md) - [Auto scoped](readme/auto-scoped.md) - [Default lifetime](readme/default-lifetime.md) +- [Disposable singleton](readme/disposable-singleton.md) +- [Async disposable singleton](readme/async-disposable-singleton.md) +- [Async disposable scope](readme/async-disposable-scope.md) ### Base Class Library - [Func](readme/func.md) - [Enumerable](readme/enumerable.md) diff --git a/readme/arguments.md b/readme/class-arguments.md similarity index 69% rename from readme/arguments.md rename to readme/class-arguments.md index 1ff28e1df..2565485f8 100644 --- a/readme/arguments.md +++ b/readme/class-arguments.md @@ -1,6 +1,6 @@ -#### Arguments +#### Class arguments -[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Basics/ArgumentsScenario.cs) +[![CSharp](https://img.shields.io/badge/C%23-code-blue.svg)](../tests/Pure.DI.UsageTests/Basics/ClassArgumentsScenario.cs) Sometimes you need to pass some state to a composition class to use it when resolving dependencies. To do this, just use the `Arg(string argName)` method, specify the type of argument and its name. You can also specify a tag for each argument. You can then use them as dependencies when building the object graph. If you have multiple arguments of the same type, just use tags to distinguish them. The values of the arguments are manipulated when you create a composition class by calling its constructor. It is important to remember that only those arguments that are used in the object graph will appear in the constructor. Arguments that are not involved will not be added to the constructor arguments. @@ -9,11 +9,15 @@ Sometimes you need to pass some state to a composition class to use it when reso interface IDependency { int Id { get; } + + string Name { get; } } -class Dependency(int id) : IDependency +class Dependency(int id, string name) : IDependency { public int Id { get; } = id; + + public string Name { get; } = name; } interface IService @@ -24,7 +28,9 @@ interface IService } class Service( - [Tag("name")] string name, + // The tag allows to specify the injection point accurately. + // This is useful, for example, when the type is the same. + [Tag("my service name")] string name, IDependency dependency) : IService { public string Name { get; } = name; @@ -42,17 +48,20 @@ DI.Setup(nameof(Composition)) // Some kind of identifier .Arg("id") - // An argument can be tagged (e.g., tag "name") + // An argument can be tagged (e.g., tag "my service name") // to be injectable by type and this tag - .Arg("serviceName", "name"); + .Arg("serviceName", "my service name") + + .Arg("dependencyName"); -var composition = new Composition(serviceName: "Abc", id: 123); +var composition = new Composition(id: 123, serviceName: "Abc", dependencyName: "Xyz"); -// service = new Service("Abc", new Dependency(123)); +// service = new Service("Abc", new Dependency(123, "Xyz")); var service = composition.Root; service.Name.ShouldBe("Abc"); service.Dependency.Id.ShouldBe(123); +service.Dependency.Name.ShouldBe("Xyz"); ``` The following partial class will be generated: @@ -64,11 +73,13 @@ partial class Composition private readonly int _arg_id; private readonly string _arg_serviceName; + private readonly string _arg_dependencyName; - public Composition(int id, string serviceName) + public Composition(int id, string serviceName, string dependencyName) { _arg_id = id; _arg_serviceName = serviceName ?? throw new ArgumentNullException(nameof(serviceName)); + _arg_dependencyName = dependencyName ?? throw new ArgumentNullException(nameof(dependencyName)); _root = this; } @@ -77,6 +88,7 @@ partial class Composition _root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root; _arg_id = _root._arg_id; _arg_serviceName = _root._arg_serviceName; + _arg_dependencyName = _root._arg_dependencyName; } public IService Root @@ -84,7 +96,7 @@ partial class Composition [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return new Service(_arg_serviceName, new Dependency(_arg_id)); + return new Service(_arg_serviceName, new Dependency(_arg_id, _arg_dependencyName)); } } } @@ -102,7 +114,7 @@ classDiagram class String Dependency --|> IDependency class Dependency { - +Dependency(Int32 id) + +Dependency(Int32 id, String name) } Service --|> IService class Service { @@ -115,7 +127,8 @@ classDiagram <> } Dependency o-- Int32 : Argument "id" - Service o-- String : "name" Argument "serviceName" + Dependency o-- String : Argument "dependencyName" + Service o-- String : "my service name" Argument "serviceName" Service *-- Dependency : IDependency Composition ..> Service : IService Root ``` diff --git a/readme/constructor-ordinal-attribute.md b/readme/constructor-ordinal-attribute.md index c8b70be18..94cd66801 100644 --- a/readme/constructor-ordinal-attribute.md +++ b/readme/constructor-ordinal-attribute.md @@ -74,120 +74,6 @@ partial class Composition return new Service(_arg_serviceName); } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Resolve() - { - return Resolver.Value.Resolve(this); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Resolve(object? tag) - { - return Resolver.Value.ResolveByTag(this, tag); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type) - { - var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1)); - ref var pair = ref _buckets[index]; - return pair.Key == type ? pair.Value.Resolve(this) : Resolve(type, index); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private object Resolve(Type type, int index) - { - var finish = index + _bucketSize; - while (++index < finish) - { - ref var pair = ref _buckets[index]; - if (pair.Key == type) - { - return pair.Value.Resolve(this); - } - } - - throw new InvalidOperationException($"{CannotResolveMessage} {OfTypeMessage} {type}."); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type, object? tag) - { - var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1)); - ref var pair = ref _buckets[index]; - return pair.Key == type ? pair.Value.ResolveByTag(this, tag) : Resolve(type, tag, index); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private object Resolve(Type type, object? tag, int index) - { - var finish = index + _bucketSize; - while (++index < finish) - { - ref var pair = ref _buckets[index]; - if (pair.Key == type) - { - return pair.Value.ResolveByTag(this, tag); - } - } - - throw new InvalidOperationException($"{CannotResolveMessage} \"{tag}\" {OfTypeMessage} {type}."); - } - - private readonly static int _bucketSize; - private readonly static Pair>[] _buckets; - - static Composition() - { - var valResolver_0000 = new Resolver_0000(); - Resolver.Value = valResolver_0000; - _buckets = Buckets>.Create( - 1, - out _bucketSize, - new Pair>[1] - { - new Pair>(typeof(IService), valResolver_0000) - }); - } - - private const string CannotResolveMessage = "Cannot resolve composition root "; - private const string OfTypeMessage = "of type "; - - private class Resolver: IResolver - { - public static IResolver Value = new Resolver(); - - public virtual T Resolve(Composition composite) - { - throw new InvalidOperationException($"{CannotResolveMessage}{OfTypeMessage}{typeof(T)}."); - } - - public virtual T ResolveByTag(Composition composite, object tag) - { - throw new InvalidOperationException($"{CannotResolveMessage}\"{tag}\" {OfTypeMessage}{typeof(T)}."); - } - } - - private sealed class Resolver_0000: Resolver - { - public override IService Resolve(Composition composition) - { - return composition.Root; - } - - public override IService ResolveByTag(Composition composition, object tag) - { - switch (tag) - { - case null: - return composition.Root; - - default: - return base.ResolveByTag(composition, tag); - } - } - } } ``` @@ -198,10 +84,6 @@ classDiagram class Composition { <> +IService Root - + T ResolveᐸTᐳ() - + T ResolveᐸTᐳ(object? tag) - + object Resolve(Type type) - + object Resolve(Type type, object? tag) } class String Service --|> IService diff --git a/readme/custom-attributes.md b/readme/custom-attributes.md index f8d3421c1..5481f807a 100644 --- a/readme/custom-attributes.md +++ b/readme/custom-attributes.md @@ -88,120 +88,6 @@ partial class PersonComposition return transient0_Person; } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Resolve() - { - return Resolver.Value.Resolve(this); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Resolve(object? tag) - { - return Resolver.Value.ResolveByTag(this, tag); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type) - { - var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1)); - ref var pair = ref _buckets[index]; - return pair.Key == type ? pair.Value.Resolve(this) : Resolve(type, index); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private object Resolve(Type type, int index) - { - var finish = index + _bucketSize; - while (++index < finish) - { - ref var pair = ref _buckets[index]; - if (pair.Key == type) - { - return pair.Value.Resolve(this); - } - } - - throw new InvalidOperationException($"{CannotResolveMessage} {OfTypeMessage} {type}."); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type, object? tag) - { - var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1)); - ref var pair = ref _buckets[index]; - return pair.Key == type ? pair.Value.ResolveByTag(this, tag) : Resolve(type, tag, index); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private object Resolve(Type type, object? tag, int index) - { - var finish = index + _bucketSize; - while (++index < finish) - { - ref var pair = ref _buckets[index]; - if (pair.Key == type) - { - return pair.Value.ResolveByTag(this, tag); - } - } - - throw new InvalidOperationException($"{CannotResolveMessage} \"{tag}\" {OfTypeMessage} {type}."); - } - - private readonly static int _bucketSize; - private readonly static Pair>[] _buckets; - - static PersonComposition() - { - var valResolver_0000 = new Resolver_0000(); - Resolver.Value = valResolver_0000; - _buckets = Buckets>.Create( - 1, - out _bucketSize, - new Pair>[1] - { - new Pair>(typeof(IPerson), valResolver_0000) - }); - } - - private const string CannotResolveMessage = "Cannot resolve composition root "; - private const string OfTypeMessage = "of type "; - - private class Resolver: IResolver - { - public static IResolver Value = new Resolver(); - - public virtual T Resolve(PersonComposition composite) - { - throw new InvalidOperationException($"{CannotResolveMessage}{OfTypeMessage}{typeof(T)}."); - } - - public virtual T ResolveByTag(PersonComposition composite, object tag) - { - throw new InvalidOperationException($"{CannotResolveMessage}\"{tag}\" {OfTypeMessage}{typeof(T)}."); - } - } - - private sealed class Resolver_0000: Resolver - { - public override IPerson Resolve(PersonComposition composition) - { - return composition.Person; - } - - public override IPerson ResolveByTag(PersonComposition composition, object tag) - { - switch (tag) - { - case null: - return composition.Person; - - default: - return base.ResolveByTag(composition, tag); - } - } - } } ``` @@ -212,10 +98,6 @@ classDiagram class PersonComposition { <> +IPerson Person - + T ResolveᐸTᐳ() - + T ResolveᐸTᐳ(object? tag) - + object Resolve(Type type) - + object Resolve(Type type, object? tag) } class String class Int32 diff --git a/readme/factory.md b/readme/factory.md index 3af3de728..03fbdebcb 100644 --- a/readme/factory.md +++ b/readme/factory.md @@ -36,12 +36,15 @@ DI.Setup(nameof(Composition)) .Bind().To(_ => DateTimeOffset.Now) .Bind().To(ctx => { + // When building a composition of objects, + // all of this code will be outside the lambda function: + // Some custom logic for creating an instance. // For example, here's how you can inject // an instance of a particular type ctx.Inject(out Dependency dependency); - // And do something about it + // And do something about it. dependency.Initialize(); // And at the end return an instance diff --git a/readme/generic-composition-roots-with-constraints.md b/readme/generic-composition-roots-with-constraints.md index b2274818c..2e564a742 100644 --- a/readme/generic-composition-roots-with-constraints.md +++ b/readme/generic-composition-roots-with-constraints.md @@ -72,21 +72,21 @@ partial class Composition } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IService GetMyRoot() - where T: IDisposable - where T4: struct + public IService GetMyRoot() + where T1: IDisposable + where T2: struct { - return new Service(new Dependency()); + return new Service(new Dependency()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IService GetOtherService() - where T: IDisposable + public IService GetOtherService() + where T1: IDisposable { - OtherService transient0_OtherService; + OtherService transient0_OtherService; { - var dependency_1 = new Dependency(); - transient0_OtherService = new OtherService(dependency_1); + var dependency_1 = new Dependency(); + transient0_OtherService = new OtherService(dependency_1); } return transient0_OtherService; } @@ -99,31 +99,31 @@ Class diagram: classDiagram class Composition { <> - +IServiceᐸTˏT4ᐳ GetMyRootᐸTˏT4ᐳ() - +IServiceᐸTˏBooleanᐳ GetOtherServiceᐸTᐳ() + +IServiceᐸT1ˏT2ᐳ GetMyRootᐸT1ˏT2ᐳ() + +IServiceᐸT1ˏBooleanᐳ GetOtherServiceᐸT1ᐳ() } - ServiceᐸTˏT4ᐳ --|> IServiceᐸTˏT4ᐳ - class ServiceᐸTˏT4ᐳ { - +Service(IDependencyᐸTᐳ dependency) + ServiceᐸT1ˏT2ᐳ --|> IServiceᐸT1ˏT2ᐳ + class ServiceᐸT1ˏT2ᐳ { + +Service(IDependencyᐸT1ᐳ dependency) } - OtherServiceᐸTᐳ --|> IServiceᐸTˏBooleanᐳ : "Other" - class OtherServiceᐸTᐳ - DependencyᐸTᐳ --|> IDependencyᐸTᐳ - class DependencyᐸTᐳ { + OtherServiceᐸT1ᐳ --|> IServiceᐸT1ˏBooleanᐳ : "Other" + class OtherServiceᐸT1ᐳ + DependencyᐸT1ᐳ --|> IDependencyᐸT1ᐳ + class DependencyᐸT1ᐳ { +Dependency() } - class IServiceᐸTˏT4ᐳ { + class IServiceᐸT1ˏT2ᐳ { <> } - class IServiceᐸTˏBooleanᐳ { + class IServiceᐸT1ˏBooleanᐳ { <> } - class IDependencyᐸTᐳ { + class IDependencyᐸT1ᐳ { <> } - Composition ..> ServiceᐸTˏT4ᐳ : IServiceᐸTˏT4ᐳ GetMyRootᐸTˏT4ᐳ() - Composition ..> OtherServiceᐸTᐳ : IServiceᐸTˏBooleanᐳ GetOtherServiceᐸTᐳ() - ServiceᐸTˏT4ᐳ *-- DependencyᐸTᐳ : IDependencyᐸTᐳ - OtherServiceᐸTᐳ *-- DependencyᐸTᐳ : IDependencyᐸTᐳ + Composition ..> ServiceᐸT1ˏT2ᐳ : IServiceᐸT1ˏT2ᐳ GetMyRootᐸT1ˏT2ᐳ() + Composition ..> OtherServiceᐸT1ᐳ : IServiceᐸT1ˏBooleanᐳ GetOtherServiceᐸT1ᐳ() + ServiceᐸT1ˏT2ᐳ *-- DependencyᐸT1ᐳ : IDependencyᐸT1ᐳ + OtherServiceᐸT1ᐳ *-- DependencyᐸT1ᐳ : IDependencyᐸT1ᐳ ``` diff --git a/readme/generic-composition-roots.md b/readme/generic-composition-roots.md index 54e7f5c93..c36df5ba7 100644 --- a/readme/generic-composition-roots.md +++ b/readme/generic-composition-roots.md @@ -69,18 +69,18 @@ partial class Composition } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IService GetMyRoot() + public IService GetMyRoot() { - return new Service(new Dependency()); + return new Service(new Dependency()); } [MethodImpl(MethodImplOptions.AggressiveInlining)] - public IService GetOtherService() + public IService GetOtherService() { - OtherService transient0_OtherService; + OtherService transient0_OtherService; { - var dependency_1 = new Dependency(); - transient0_OtherService = new OtherService(dependency_1); + var dependency_1 = new Dependency(); + transient0_OtherService = new OtherService(dependency_1); } return transient0_OtherService; } @@ -93,28 +93,28 @@ Class diagram: classDiagram class Composition { <> - +IServiceᐸT54ᐳ GetMyRootᐸT54ᐳ() - +IServiceᐸT54ᐳ GetOtherServiceᐸT54ᐳ() + +IServiceᐸT3ᐳ GetMyRootᐸT3ᐳ() + +IServiceᐸT3ᐳ GetOtherServiceᐸT3ᐳ() } - ServiceᐸT54ᐳ --|> IServiceᐸT54ᐳ - class ServiceᐸT54ᐳ { - +Service(IDependencyᐸT54ᐳ dependency) + ServiceᐸT3ᐳ --|> IServiceᐸT3ᐳ + class ServiceᐸT3ᐳ { + +Service(IDependencyᐸT3ᐳ dependency) } - OtherServiceᐸT54ᐳ --|> IServiceᐸT54ᐳ : "Other" - class OtherServiceᐸT54ᐳ - DependencyᐸT54ᐳ --|> IDependencyᐸT54ᐳ - class DependencyᐸT54ᐳ { + OtherServiceᐸT3ᐳ --|> IServiceᐸT3ᐳ : "Other" + class OtherServiceᐸT3ᐳ + DependencyᐸT3ᐳ --|> IDependencyᐸT3ᐳ + class DependencyᐸT3ᐳ { +Dependency() } - class IServiceᐸT54ᐳ { + class IServiceᐸT3ᐳ { <> } - class IDependencyᐸT54ᐳ { + class IDependencyᐸT3ᐳ { <> } - Composition ..> ServiceᐸT54ᐳ : IServiceᐸT54ᐳ GetMyRootᐸT54ᐳ() - Composition ..> OtherServiceᐸT54ᐳ : IServiceᐸT54ᐳ GetOtherServiceᐸT54ᐳ() - ServiceᐸT54ᐳ *-- DependencyᐸT54ᐳ : IDependencyᐸT54ᐳ - OtherServiceᐸT54ᐳ *-- DependencyᐸT54ᐳ : IDependencyᐸT54ᐳ + Composition ..> ServiceᐸT3ᐳ : IServiceᐸT3ᐳ GetMyRootᐸT3ᐳ() + Composition ..> OtherServiceᐸT3ᐳ : IServiceᐸT3ᐳ GetOtherServiceᐸT3ᐳ() + ServiceᐸT3ᐳ *-- DependencyᐸT3ᐳ : IDependencyᐸT3ᐳ + OtherServiceᐸT3ᐳ *-- DependencyᐸT3ᐳ : IDependencyᐸT3ᐳ ``` diff --git a/readme/member-ordinal-attribute.md b/readme/member-ordinal-attribute.md index ccb2ea296..51dcf26f2 100644 --- a/readme/member-ordinal-attribute.md +++ b/readme/member-ordinal-attribute.md @@ -104,120 +104,6 @@ partial class PersonComposition return transient0_Person; } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Resolve() - { - return Resolver.Value.Resolve(this); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Resolve(object? tag) - { - return Resolver.Value.ResolveByTag(this, tag); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type) - { - var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1)); - ref var pair = ref _buckets[index]; - return pair.Key == type ? pair.Value.Resolve(this) : Resolve(type, index); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private object Resolve(Type type, int index) - { - var finish = index + _bucketSize; - while (++index < finish) - { - ref var pair = ref _buckets[index]; - if (pair.Key == type) - { - return pair.Value.Resolve(this); - } - } - - throw new InvalidOperationException($"{CannotResolveMessage} {OfTypeMessage} {type}."); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type, object? tag) - { - var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1)); - ref var pair = ref _buckets[index]; - return pair.Key == type ? pair.Value.ResolveByTag(this, tag) : Resolve(type, tag, index); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private object Resolve(Type type, object? tag, int index) - { - var finish = index + _bucketSize; - while (++index < finish) - { - ref var pair = ref _buckets[index]; - if (pair.Key == type) - { - return pair.Value.ResolveByTag(this, tag); - } - } - - throw new InvalidOperationException($"{CannotResolveMessage} \"{tag}\" {OfTypeMessage} {type}."); - } - - private readonly static int _bucketSize; - private readonly static Pair>[] _buckets; - - static PersonComposition() - { - var valResolver_0000 = new Resolver_0000(); - Resolver.Value = valResolver_0000; - _buckets = Buckets>.Create( - 1, - out _bucketSize, - new Pair>[1] - { - new Pair>(typeof(IPerson), valResolver_0000) - }); - } - - private const string CannotResolveMessage = "Cannot resolve composition root "; - private const string OfTypeMessage = "of type "; - - private class Resolver: IResolver - { - public static IResolver Value = new Resolver(); - - public virtual T Resolve(PersonComposition composite) - { - throw new InvalidOperationException($"{CannotResolveMessage}{OfTypeMessage}{typeof(T)}."); - } - - public virtual T ResolveByTag(PersonComposition composite, object tag) - { - throw new InvalidOperationException($"{CannotResolveMessage}\"{tag}\" {OfTypeMessage}{typeof(T)}."); - } - } - - private sealed class Resolver_0000: Resolver - { - public override IPerson Resolve(PersonComposition composition) - { - return composition.Person; - } - - public override IPerson ResolveByTag(PersonComposition composition, object tag) - { - switch (tag) - { - case null: - return composition.Person; - - default: - return base.ResolveByTag(composition, tag); - } - } - } } ``` @@ -228,10 +114,6 @@ classDiagram class PersonComposition { <> +IPerson Person - + T ResolveᐸTᐳ() - + T ResolveᐸTᐳ(object? tag) - + object Resolve(Type type) - + object Resolve(Type type, object? tag) } class Int32 class String diff --git a/readme/perblock.md b/readme/perblock.md index 2d57a3785..852810ceb 100644 --- a/readme/perblock.md +++ b/readme/perblock.md @@ -13,21 +13,22 @@ class Dependency : IDependency; class Service( IDependency dep1, IDependency dep2, - Lazy<(IDependency dep3, IDependency dep4)> deps) + (IDependency dep3, IDependency dep4) deps) { public IDependency Dep1 { get; } = dep1; public IDependency Dep2 { get; } = dep2; - public IDependency Dep3 { get; } = deps.Value.dep3; + public IDependency Dep3 { get; } = deps.dep3; - public IDependency Dep4 { get; } = deps.Value.dep4; + public IDependency Dep4 { get; } = deps.dep4; } DI.Setup(nameof(Composition)) // This hint indicates to not generate methods such as Resolve .Hint(Hint.Resolve, "Off") .Bind().As(Lifetime.PerBlock).To() + .Bind().As(Lifetime.Singleton).To<(IDependency dep3, IDependency dep4)>() // Composition root .Root("Root"); @@ -50,6 +51,8 @@ partial class Composition { private readonly Composition _root; private readonly object _lock; + private (IDependency dep3, IDependency dep4) _singleton37_ValueTuple; + private bool _singleton37_ValueTupleCreated; public Composition() { @@ -68,22 +71,21 @@ partial class Composition [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - var perResolve43_Func = default(Func<(IDependency dep3, IDependency dep4)>); - perResolve43_Func = new Func<(IDependency dep3, IDependency dep4)>( - [MethodImpl(MethodImplOptions.AggressiveInlining)] - () => + if (!_root._singleton37_ValueTupleCreated) { - Dependency perBlock4_Dependency = new Dependency(); - var value_1 = (perBlock4_Dependency, perBlock4_Dependency); - return value_1; - }); - Lazy<(IDependency dep3, IDependency dep4)> transient2_Lazy; - { - var factory_2 = perResolve43_Func!; - transient2_Lazy = new Lazy<(IDependency dep3, IDependency dep4)>(factory_2, true); + lock (_lock) + { + if (!_root._singleton37_ValueTupleCreated) + { + Dependency perBlock2_Dependency = new Dependency(); + _root._singleton37_ValueTuple = (perBlock2_Dependency, perBlock2_Dependency); + Thread.MemoryBarrier(); + _root._singleton37_ValueTupleCreated = true; + } + } } Dependency perBlock1_Dependency = new Dependency(); - return new Service(perBlock1_Dependency, perBlock1_Dependency, transient2_Lazy); + return new Service(perBlock1_Dependency, perBlock1_Dependency, _root._singleton37_ValueTuple); } } } @@ -97,26 +99,22 @@ classDiagram <> +Service Root } - class ValueTupleᐸIDependencyˏIDependencyᐳ { - +ValueTuple(IDependency item1, IDependency item2) - } class Service { - +Service(IDependency dep1, IDependency dep2, LazyᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ deps) + +Service(IDependency dep1, IDependency dep2, ValueTupleᐸIDependencyˏIDependencyᐳ deps) } Dependency --|> IDependency class Dependency { +Dependency() } - class LazyᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ - class FuncᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ + class ValueTupleᐸIDependencyˏIDependencyᐳ { + +ValueTuple(IDependency item1, IDependency item2) + } class IDependency { <> } - ValueTupleᐸIDependencyˏIDependencyᐳ o-- "2 PerBlock" Dependency : IDependency Service o-- "2 PerBlock" Dependency : IDependency - Service *-- LazyᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ : LazyᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ + Service o-- "Singleton" ValueTupleᐸIDependencyˏIDependencyᐳ : ValueTupleᐸIDependencyˏIDependencyᐳ + ValueTupleᐸIDependencyˏIDependencyᐳ o-- "2 PerBlock" Dependency : IDependency Composition ..> Service : Service Root - LazyᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ o-- "PerResolve" FuncᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ : FuncᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ - FuncᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ *-- ValueTupleᐸIDependencyˏIDependencyᐳ : ValueTupleᐸIDependencyˏIDependencyᐳ ``` diff --git a/readme/perresolve.md b/readme/perresolve.md index 806b2993b..33a1e4258 100644 --- a/readme/perresolve.md +++ b/readme/perresolve.md @@ -13,21 +13,22 @@ class Dependency : IDependency; class Service( IDependency dep1, IDependency dep2, - Lazy<(IDependency dep3, IDependency dep4)> deps) + (IDependency dep3, IDependency dep4) deps) { public IDependency Dep1 { get; } = dep1; public IDependency Dep2 { get; } = dep2; - public IDependency Dep3 { get; } = deps.Value.dep3; + public IDependency Dep3 { get; } = deps.dep3; - public IDependency Dep4 { get; } = deps.Value.dep4; + public IDependency Dep4 { get; } = deps.dep4; } DI.Setup(nameof(Composition)) // This hint indicates to not generate methods such as Resolve .Hint(Hint.Resolve, "Off") .Bind().As(Lifetime.PerResolve).To() + .Bind().As(Lifetime.Singleton).To<(IDependency dep3, IDependency dep4)>() // Composition root .Root("Root"); @@ -50,6 +51,8 @@ partial class Composition { private readonly Composition _root; private readonly object _lock; + private (IDependency dep3, IDependency dep4) _singleton37_ValueTuple; + private bool _singleton37_ValueTupleCreated; public Composition() { @@ -69,40 +72,27 @@ partial class Composition get { var perResolve36_Dependency = default(Dependency); - var perResolve43_Func = default(Func<(IDependency dep3, IDependency dep4)>); - perResolve43_Func = new Func<(IDependency dep3, IDependency dep4)>( - [MethodImpl(MethodImplOptions.AggressiveInlining)] - () => + if (!_root._singleton37_ValueTupleCreated) { - if (perResolve36_Dependency == null) + lock (_lock) { - lock (_lock) + if (!_root._singleton37_ValueTupleCreated) { if (perResolve36_Dependency == null) { perResolve36_Dependency = new Dependency(); } + _root._singleton37_ValueTuple = (perResolve36_Dependency!, perResolve36_Dependency!); + Thread.MemoryBarrier(); + _root._singleton37_ValueTupleCreated = true; } } - var value_1 = (perResolve36_Dependency!, perResolve36_Dependency!); - return value_1; - }); - Lazy<(IDependency dep3, IDependency dep4)> transient1_Lazy; - { - var factory_2 = perResolve43_Func!; - transient1_Lazy = new Lazy<(IDependency dep3, IDependency dep4)>(factory_2, true); } if (perResolve36_Dependency == null) { - lock (_lock) - { - if (perResolve36_Dependency == null) - { - perResolve36_Dependency = new Dependency(); - } - } + perResolve36_Dependency = new Dependency(); } - return new Service(perResolve36_Dependency!, perResolve36_Dependency!, transient1_Lazy); + return new Service(perResolve36_Dependency!, perResolve36_Dependency!, _root._singleton37_ValueTuple); } } } @@ -116,26 +106,22 @@ classDiagram <> +Service Root } - class ValueTupleᐸIDependencyˏIDependencyᐳ { - +ValueTuple(IDependency item1, IDependency item2) - } class Service { - +Service(IDependency dep1, IDependency dep2, LazyᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ deps) + +Service(IDependency dep1, IDependency dep2, ValueTupleᐸIDependencyˏIDependencyᐳ deps) } Dependency --|> IDependency class Dependency { +Dependency() } - class LazyᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ - class FuncᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ + class ValueTupleᐸIDependencyˏIDependencyᐳ { + +ValueTuple(IDependency item1, IDependency item2) + } class IDependency { <> } - ValueTupleᐸIDependencyˏIDependencyᐳ o-- "2 PerResolve" Dependency : IDependency Service o-- "2 PerResolve" Dependency : IDependency - Service *-- LazyᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ : LazyᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ + Service o-- "Singleton" ValueTupleᐸIDependencyˏIDependencyᐳ : ValueTupleᐸIDependencyˏIDependencyᐳ + ValueTupleᐸIDependencyˏIDependencyᐳ o-- "2 PerResolve" Dependency : IDependency Composition ..> Service : Service Root - LazyᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ o-- "PerResolve" FuncᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ : FuncᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ - FuncᐸValueTupleᐸIDependencyˏIDependencyᐳᐳ *-- ValueTupleᐸIDependencyˏIDependencyᐳ : ValueTupleᐸIDependencyˏIDependencyᐳ ``` diff --git a/readme/root-binding.md b/readme/root-binding.md index f4d6b739c..dbf413aa9 100644 --- a/readme/root-binding.md +++ b/readme/root-binding.md @@ -6,11 +6,16 @@ You might want to register some services as roots. You can use `RootBind()` m ```c# +interface IDependency; + +class Dependency: IDependency; + interface IService; -class Service : IService; +class Service(IDependency dependency) : IService; DI.Setup(nameof(Composition)) + .Bind().As(Lifetime.Singleton).To() .RootBind("Root").To(); var composition = new Composition(); @@ -23,15 +28,19 @@ The following partial class will be generated: partial class Composition { private readonly Composition _root; + private readonly object _lock; + private Dependency? _singleton36_Dependency; public Composition() { _root = this; + _lock = new object(); } internal Composition(Composition parentScope) { _root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root; + _lock = _root._lock; } public IService Root @@ -39,7 +48,17 @@ partial class Composition [MethodImpl(MethodImplOptions.AggressiveInlining)] get { - return new Service(); + if (_root._singleton36_Dependency == null) + { + lock (_lock) + { + if (_root._singleton36_Dependency == null) + { + _root._singleton36_Dependency = new Dependency(); + } + } + } + return new Service(_root._singleton36_Dependency!); } } } @@ -53,13 +72,21 @@ classDiagram <> +IService Root } + Dependency --|> IDependency + class Dependency { + +Dependency() + } Service --|> IService class Service { - +Service() + +Service(IDependency dependency) + } + class IDependency { + <> } class IService { <> } + Service o-- "Singleton" Dependency : IDependency Composition ..> Service : IService Root ``` diff --git a/readme/simplified-binding.md b/readme/simplified-binding.md index 9c9d1394d..738f217db 100644 --- a/readme/simplified-binding.md +++ b/readme/simplified-binding.md @@ -12,10 +12,13 @@ interface IOtherDependency; class Dependency: IDependency, IOtherDependency; +interface IService; + class Service( Dependency dependencyImpl, IDependency dependency, - IOtherDependency otherDependency); + IOtherDependency otherDependency) + : IService; // Specifies to create a partial class "Composition" DI.Setup("Composition") @@ -27,9 +30,10 @@ DI.Setup("Composition") // .As(Lifetime.PerBlock) // .To() .Bind().As(Lifetime.PerBlock).To() + .Bind().To() // Specifies to create a property "MyService" - .Root("MyService"); + .Root("MyService"); var composition = new Composition(); var service = composition.MyService; @@ -70,7 +74,7 @@ partial class Composition _root = (parentScope ?? throw new ArgumentNullException(nameof(parentScope)))._root; } - public Service MyService + public IService MyService { [MethodImpl(MethodImplOptions.AggressiveInlining)] get @@ -88,25 +92,29 @@ Class diagram: classDiagram class Composition { <> - +Service MyService - } - class Service { - +Service(Dependency dependencyImpl, IDependency dependency, IOtherDependency otherDependency) + +IService MyService } Dependency --|> IDependency Dependency --|> IOtherDependency class Dependency { +Dependency() } + Service --|> IService + class Service { + +Service(Dependency dependencyImpl, IDependency dependency, IOtherDependency otherDependency) + } class IDependency { <> } class IOtherDependency { <> } + class IService { + <> + } Service o-- "PerBlock" Dependency : Dependency Service o-- "PerBlock" Dependency : IDependency Service o-- "PerBlock" Dependency : IOtherDependency - Composition ..> Service : Service MyService + Composition ..> Service : IService MyService ``` diff --git a/readme/tag-attribute.md b/readme/tag-attribute.md index 817ac1bcb..d21c4a14b 100644 --- a/readme/tag-attribute.md +++ b/readme/tag-attribute.md @@ -72,120 +72,6 @@ partial class Composition return new Service(new AbcDependency(), new XyzDependency()); } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Resolve() - { - return Resolver.Value.Resolve(this); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Resolve(object? tag) - { - return Resolver.Value.ResolveByTag(this, tag); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type) - { - var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1)); - ref var pair = ref _buckets[index]; - return pair.Key == type ? pair.Value.Resolve(this) : Resolve(type, index); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private object Resolve(Type type, int index) - { - var finish = index + _bucketSize; - while (++index < finish) - { - ref var pair = ref _buckets[index]; - if (pair.Key == type) - { - return pair.Value.Resolve(this); - } - } - - throw new InvalidOperationException($"{CannotResolveMessage} {OfTypeMessage} {type}."); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type, object? tag) - { - var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1)); - ref var pair = ref _buckets[index]; - return pair.Key == type ? pair.Value.ResolveByTag(this, tag) : Resolve(type, tag, index); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private object Resolve(Type type, object? tag, int index) - { - var finish = index + _bucketSize; - while (++index < finish) - { - ref var pair = ref _buckets[index]; - if (pair.Key == type) - { - return pair.Value.ResolveByTag(this, tag); - } - } - - throw new InvalidOperationException($"{CannotResolveMessage} \"{tag}\" {OfTypeMessage} {type}."); - } - - private readonly static int _bucketSize; - private readonly static Pair>[] _buckets; - - static Composition() - { - var valResolver_0000 = new Resolver_0000(); - Resolver.Value = valResolver_0000; - _buckets = Buckets>.Create( - 1, - out _bucketSize, - new Pair>[1] - { - new Pair>(typeof(IService), valResolver_0000) - }); - } - - private const string CannotResolveMessage = "Cannot resolve composition root "; - private const string OfTypeMessage = "of type "; - - private class Resolver: IResolver - { - public static IResolver Value = new Resolver(); - - public virtual T Resolve(Composition composite) - { - throw new InvalidOperationException($"{CannotResolveMessage}{OfTypeMessage}{typeof(T)}."); - } - - public virtual T ResolveByTag(Composition composite, object tag) - { - throw new InvalidOperationException($"{CannotResolveMessage}\"{tag}\" {OfTypeMessage}{typeof(T)}."); - } - } - - private sealed class Resolver_0000: Resolver - { - public override IService Resolve(Composition composition) - { - return composition.Root; - } - - public override IService ResolveByTag(Composition composition, object tag) - { - switch (tag) - { - case null: - return composition.Root; - - default: - return base.ResolveByTag(composition, tag); - } - } - } } ``` @@ -196,10 +82,6 @@ classDiagram class Composition { <> +IService Root - + T ResolveᐸTᐳ() - + T ResolveᐸTᐳ(object? tag) - + object Resolve(Type type) - + object Resolve(Type type, object? tag) } AbcDependency --|> IDependency : "Abc" class AbcDependency { diff --git a/readme/type-attribute.md b/readme/type-attribute.md index ba9caf8a6..da6aa556a 100644 --- a/readme/type-attribute.md +++ b/readme/type-attribute.md @@ -68,120 +68,6 @@ partial class Composition return new Service(new AbcDependency(), new XyzDependency()); } } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Resolve() - { - return Resolver.Value.Resolve(this); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public T Resolve(object? tag) - { - return Resolver.Value.ResolveByTag(this, tag); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type) - { - var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1)); - ref var pair = ref _buckets[index]; - return pair.Key == type ? pair.Value.Resolve(this) : Resolve(type, index); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private object Resolve(Type type, int index) - { - var finish = index + _bucketSize; - while (++index < finish) - { - ref var pair = ref _buckets[index]; - if (pair.Key == type) - { - return pair.Value.Resolve(this); - } - } - - throw new InvalidOperationException($"{CannotResolveMessage} {OfTypeMessage} {type}."); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public object Resolve(Type type, object? tag) - { - var index = (int)(_bucketSize * ((uint)RuntimeHelpers.GetHashCode(type) % 1)); - ref var pair = ref _buckets[index]; - return pair.Key == type ? pair.Value.ResolveByTag(this, tag) : Resolve(type, tag, index); - } - - [MethodImpl(MethodImplOptions.NoInlining)] - private object Resolve(Type type, object? tag, int index) - { - var finish = index + _bucketSize; - while (++index < finish) - { - ref var pair = ref _buckets[index]; - if (pair.Key == type) - { - return pair.Value.ResolveByTag(this, tag); - } - } - - throw new InvalidOperationException($"{CannotResolveMessage} \"{tag}\" {OfTypeMessage} {type}."); - } - - private readonly static int _bucketSize; - private readonly static Pair>[] _buckets; - - static Composition() - { - var valResolver_0000 = new Resolver_0000(); - Resolver.Value = valResolver_0000; - _buckets = Buckets>.Create( - 1, - out _bucketSize, - new Pair>[1] - { - new Pair>(typeof(IService), valResolver_0000) - }); - } - - private const string CannotResolveMessage = "Cannot resolve composition root "; - private const string OfTypeMessage = "of type "; - - private class Resolver: IResolver - { - public static IResolver Value = new Resolver(); - - public virtual T Resolve(Composition composite) - { - throw new InvalidOperationException($"{CannotResolveMessage}{OfTypeMessage}{typeof(T)}."); - } - - public virtual T ResolveByTag(Composition composite, object tag) - { - throw new InvalidOperationException($"{CannotResolveMessage}\"{tag}\" {OfTypeMessage}{typeof(T)}."); - } - } - - private sealed class Resolver_0000: Resolver - { - public override IService Resolve(Composition composition) - { - return composition.Root; - } - - public override IService ResolveByTag(Composition composition, object tag) - { - switch (tag) - { - case null: - return composition.Root; - - default: - return base.ResolveByTag(composition, tag); - } - } - } } ``` @@ -192,10 +78,6 @@ classDiagram class Composition { <> +IService Root - + T ResolveᐸTᐳ() - + T ResolveᐸTᐳ(object? tag) - + object Resolve(Type type) - + object Resolve(Type type, object? tag) } class XyzDependency { +XyzDependency() diff --git a/src/Pure.DI.Core/Core/Code/TypeResolver.cs b/src/Pure.DI.Core/Core/Code/TypeResolver.cs index ed86ec063..649a81248 100644 --- a/src/Pure.DI.Core/Core/Code/TypeResolver.cs +++ b/src/Pure.DI.Core/Core/Code/TypeResolver.cs @@ -1,12 +1,13 @@ // ReSharper disable ClassNeverInstantiated.Global namespace Pure.DI.Core.Code; -internal class TypeResolver(IMarker marker) +internal class TypeResolver( + IMarker marker, + [Tag("GenericType")] IIdGenerator idGenerator) : ITypeResolver { private readonly Dictionary _names = new(SymbolEqualityComparer.Default); - private int _markerCounter; - + public TypeDescription Resolve(ITypeSymbol type) => Resolve(type, default); private TypeDescription Resolve(ITypeSymbol type, ITypeParameterSymbol? typeParam) @@ -19,11 +20,11 @@ private TypeDescription Resolve(ITypeSymbol type, ITypeParameterSymbol? typePara { if (!_names.TryGetValue(type, out var typeName)) { - typeName = _markerCounter == 0 ? "T" : $"T{_markerCounter}"; + var id = idGenerator.Generate(); + typeName = id == 0 ? "T" : $"T{id}"; _names.Add(type, typeName); } - _markerCounter++; description = new TypeDescription(typeName, ImmutableArray.Create(new TypeDescription(typeName, ImmutableArray.Empty, typeParam)), typeParam); } else diff --git a/src/Pure.DI.Core/Generator.Composition.cs b/src/Pure.DI.Core/Generator.Composition.cs index 76c8f639f..00eabbd0a 100644 --- a/src/Pure.DI.Core/Generator.Composition.cs +++ b/src/Pure.DI.Core/Generator.Composition.cs @@ -123,5 +123,6 @@ private void Setup() => DI.Setup(nameof(Generator)) .Bind().To() .Bind().To() .Bind("UniqueTags").To() + .Bind("GenericType").To() .Bind().To(); } \ No newline at end of file diff --git a/tests/Pure.DI.UsageTests/Attributes/ConstructorOrdinalAttributeScenario.cs b/tests/Pure.DI.UsageTests/Attributes/ConstructorOrdinalAttributeScenario.cs index 7ab0006db..e8ff591b9 100644 --- a/tests/Pure.DI.UsageTests/Attributes/ConstructorOrdinalAttributeScenario.cs +++ b/tests/Pure.DI.UsageTests/Attributes/ConstructorOrdinalAttributeScenario.cs @@ -48,6 +48,7 @@ public class Scenario [Fact] public void Run() { + // Resolve = Off // { DI.Setup(nameof(Composition)) .Arg("serviceName") diff --git a/tests/Pure.DI.UsageTests/Attributes/CustomAttributesScenario.cs b/tests/Pure.DI.UsageTests/Attributes/CustomAttributesScenario.cs index e35419097..8e67fe288 100644 --- a/tests/Pure.DI.UsageTests/Attributes/CustomAttributesScenario.cs +++ b/tests/Pure.DI.UsageTests/Attributes/CustomAttributesScenario.cs @@ -56,6 +56,7 @@ public class Scenario [Fact] public void Run() { + // Resolve = Off // { DI.Setup(nameof(PersonComposition)) .TagAttribute() diff --git a/tests/Pure.DI.UsageTests/Attributes/MemberOrdinalAttributeScenario.cs b/tests/Pure.DI.UsageTests/Attributes/MemberOrdinalAttributeScenario.cs index b40c9634c..ba69dd4e7 100644 --- a/tests/Pure.DI.UsageTests/Attributes/MemberOrdinalAttributeScenario.cs +++ b/tests/Pure.DI.UsageTests/Attributes/MemberOrdinalAttributeScenario.cs @@ -62,6 +62,7 @@ public class Scenario [Fact] public void Run() { + // Resolve = Off // { DI.Setup(nameof(PersonComposition)) .Arg("personId") diff --git a/tests/Pure.DI.UsageTests/Attributes/TagAttributeScenario.cs b/tests/Pure.DI.UsageTests/Attributes/TagAttributeScenario.cs index 6723a7d86..fab7042e5 100644 --- a/tests/Pure.DI.UsageTests/Attributes/TagAttributeScenario.cs +++ b/tests/Pure.DI.UsageTests/Attributes/TagAttributeScenario.cs @@ -47,6 +47,7 @@ public class Scenario [Fact] public void Run() { + // Resolve = Off // { DI.Setup(nameof(Composition)) .Bind("Abc").To() diff --git a/tests/Pure.DI.UsageTests/Attributes/TypeAttributeScenario.cs b/tests/Pure.DI.UsageTests/Attributes/TypeAttributeScenario.cs index db290fd9c..0d21b501e 100644 --- a/tests/Pure.DI.UsageTests/Attributes/TypeAttributeScenario.cs +++ b/tests/Pure.DI.UsageTests/Attributes/TypeAttributeScenario.cs @@ -47,6 +47,7 @@ public class Scenario [Fact] public void Run() { + // Resolve = Off // { DI.Setup(nameof(Composition)) .Bind().To() diff --git a/tests/Pure.DI.UsageTests/Basics/ArgumentsScenario.cs b/tests/Pure.DI.UsageTests/Basics/ClassArgumentsScenario.cs similarity index 71% rename from tests/Pure.DI.UsageTests/Basics/ArgumentsScenario.cs rename to tests/Pure.DI.UsageTests/Basics/ClassArgumentsScenario.cs index 3c496d121..d0a6e150c 100644 --- a/tests/Pure.DI.UsageTests/Basics/ArgumentsScenario.cs +++ b/tests/Pure.DI.UsageTests/Basics/ClassArgumentsScenario.cs @@ -1,7 +1,7 @@ /* $v=true $p=5 -$d=Arguments +$d=Class arguments $h=Sometimes you need to pass some state to a composition class to use it when resolving dependencies. To do this, just use the `Arg(string argName)` method, specify the type of argument and its name. You can also specify a tag for each argument. You can then use them as dependencies when building the object graph. If you have multiple arguments of the same type, just use tags to distinguish them. The values of the arguments are manipulated when you create a composition class by calling its constructor. It is important to remember that only those arguments that are used in the object graph will appear in the constructor. Arguments that are not involved will not be added to the constructor arguments. */ @@ -9,7 +9,7 @@ // ReSharper disable CheckNamespace // ReSharper disable UnusedParameter.Local // ReSharper disable ArrangeTypeModifiers -namespace Pure.DI.UsageTests.Basics.ArgumentsScenario; +namespace Pure.DI.UsageTests.Basics.ClassArgumentsScenario; using Shouldly; using Xunit; @@ -18,11 +18,15 @@ namespace Pure.DI.UsageTests.Basics.ArgumentsScenario; interface IDependency { int Id { get; } + + string Name { get; } } -class Dependency(int id) : IDependency +class Dependency(int id, string name) : IDependency { public int Id { get; } = id; + + public string Name { get; } = name; } interface IService @@ -33,7 +37,9 @@ interface IService } class Service( - [Tag("name")] string name, + // The tag allows to specify the injection point accurately. + // This is useful, for example, when the type is the same. + [Tag("my service name")] string name, IDependency dependency) : IService { public string Name { get; } = name; @@ -59,17 +65,20 @@ public void Run() // Some kind of identifier .Arg("id") - // An argument can be tagged (e.g., tag "name") + // An argument can be tagged (e.g., tag "my service name") // to be injectable by type and this tag - .Arg("serviceName", "name"); + .Arg("serviceName", "my service name") + + .Arg("dependencyName"); - var composition = new Composition(serviceName: "Abc", id: 123); + var composition = new Composition(id: 123, serviceName: "Abc", dependencyName: "Xyz"); - // service = new Service("Abc", new Dependency(123)); + // service = new Service("Abc", new Dependency(123, "Xyz")); var service = composition.Root; service.Name.ShouldBe("Abc"); service.Dependency.Id.ShouldBe(123); + service.Dependency.Name.ShouldBe("Xyz"); // } composition.SaveClassDiagram(); } diff --git a/tests/Pure.DI.UsageTests/Basics/FactoryScenario.cs b/tests/Pure.DI.UsageTests/Basics/FactoryScenario.cs index d15bd62d4..11b24bcf6 100644 --- a/tests/Pure.DI.UsageTests/Basics/FactoryScenario.cs +++ b/tests/Pure.DI.UsageTests/Basics/FactoryScenario.cs @@ -54,12 +54,15 @@ public void Run() .Bind().To(_ => DateTimeOffset.Now) .Bind().To(ctx => { + // When building a composition of objects, + // all of this code will be outside the lambda function: + // Some custom logic for creating an instance. // For example, here's how you can inject // an instance of a particular type ctx.Inject(out Dependency dependency); - // And do something about it + // And do something about it. dependency.Initialize(); // And at the end return an instance diff --git a/tests/Pure.DI.UsageTests/Basics/RootBindScenario.cs b/tests/Pure.DI.UsageTests/Basics/RootBindScenario.cs index ffd1aa51e..731937f36 100644 --- a/tests/Pure.DI.UsageTests/Basics/RootBindScenario.cs +++ b/tests/Pure.DI.UsageTests/Basics/RootBindScenario.cs @@ -9,15 +9,20 @@ // ReSharper disable CheckNamespace // ReSharper disable UnusedParameter.Local // ReSharper disable ArrangeTypeModifiers +#pragma warning disable CS9113 // Parameter is unread. namespace Pure.DI.UsageTests.Basics.RootBindScenario; using Shouldly; using Xunit; // { +interface IDependency; + +class Dependency: IDependency; + interface IService; -class Service : IService; +class Service(IDependency dependency) : IService; // } public class Scenario @@ -28,6 +33,7 @@ public void Run() // Resolve = Off // { DI.Setup(nameof(Composition)) + .Bind().As(Lifetime.Singleton).To() .RootBind("Root").To(); var composition = new Composition(); diff --git a/tests/Pure.DI.UsageTests/Basics/SimplifiedBindingScenario.cs b/tests/Pure.DI.UsageTests/Basics/SimplifiedBindingScenario.cs index 886345033..465998c03 100644 --- a/tests/Pure.DI.UsageTests/Basics/SimplifiedBindingScenario.cs +++ b/tests/Pure.DI.UsageTests/Basics/SimplifiedBindingScenario.cs @@ -39,10 +39,13 @@ interface IOtherDependency; class Dependency: IDependency, IOtherDependency; +interface IService; + class Service( Dependency dependencyImpl, IDependency dependency, - IOtherDependency otherDependency); + IOtherDependency otherDependency) + : IService; // } public class Scenario @@ -62,9 +65,10 @@ public void Run() // .As(Lifetime.PerBlock) // .To() .Bind().As(Lifetime.PerBlock).To() + .Bind().To() // Specifies to create a property "MyService" - .Root("MyService"); + .Root("MyService"); var composition = new Composition(); var service = composition.MyService; diff --git a/tests/Pure.DI.UsageTests/Lifetimes/AsyncDisposableScopeScenario.cs b/tests/Pure.DI.UsageTests/Lifetimes/AsyncDisposableScopeScenario.cs index e06acc057..f331de05e 100644 --- a/tests/Pure.DI.UsageTests/Lifetimes/AsyncDisposableScopeScenario.cs +++ b/tests/Pure.DI.UsageTests/Lifetimes/AsyncDisposableScopeScenario.cs @@ -1,6 +1,6 @@ /* $v=true -$p=4 +$p=9 $d=Async disposable scope */ diff --git a/tests/Pure.DI.UsageTests/Lifetimes/AsyncDisposableSingletonScenario.cs b/tests/Pure.DI.UsageTests/Lifetimes/AsyncDisposableSingletonScenario.cs index a72df9b48..5d9486581 100644 --- a/tests/Pure.DI.UsageTests/Lifetimes/AsyncDisposableSingletonScenario.cs +++ b/tests/Pure.DI.UsageTests/Lifetimes/AsyncDisposableSingletonScenario.cs @@ -1,6 +1,6 @@ /* $v=true -$p=4 +$p=8 $d=Async disposable singleton $h=If at least one of these objects implements the `IAsyncDisposable` interface, then the composition implements `IAsyncDisposable` as well. To dispose of all created singleton instances in an asynchronous manner, simply dispose of the composition instance in an asynchronous manner: */ diff --git a/tests/Pure.DI.UsageTests/Lifetimes/DefaultLifetimeScenario.cs b/tests/Pure.DI.UsageTests/Lifetimes/DefaultLifetimeScenario.cs index b572d858f..af72b9d3c 100644 --- a/tests/Pure.DI.UsageTests/Lifetimes/DefaultLifetimeScenario.cs +++ b/tests/Pure.DI.UsageTests/Lifetimes/DefaultLifetimeScenario.cs @@ -1,6 +1,6 @@ /* $v=true -$p=5 +$p=6 $d=Default lifetime $h=For example, if some lifetime is used more often than others, you can make it the default lifetime: */ diff --git a/tests/Pure.DI.UsageTests/Lifetimes/DisposableSingletonScenario.cs b/tests/Pure.DI.UsageTests/Lifetimes/DisposableSingletonScenario.cs index 3856ad125..163ee4d5a 100644 --- a/tests/Pure.DI.UsageTests/Lifetimes/DisposableSingletonScenario.cs +++ b/tests/Pure.DI.UsageTests/Lifetimes/DisposableSingletonScenario.cs @@ -1,6 +1,6 @@ /* $v=true -$p=3 +$p=7 $d=Disposable singleton $h=To dispose all created singleton instances, simply dispose the composition instance: $f=A composition class becomes disposable if it creates at least one disposable singleton instance. diff --git a/tests/Pure.DI.UsageTests/Lifetimes/PerBlockScenario.cs b/tests/Pure.DI.UsageTests/Lifetimes/PerBlockScenario.cs index 9939b2d68..9689f6c0f 100644 --- a/tests/Pure.DI.UsageTests/Lifetimes/PerBlockScenario.cs +++ b/tests/Pure.DI.UsageTests/Lifetimes/PerBlockScenario.cs @@ -1,6 +1,6 @@ /* $v=true -$p=2 +$p=3 $d=PerBlock $h=The _PreBlock_ lifetime does not guarantee that there will be a single instance of the dependency for each root of the composition, but is useful to reduce the number of instances of type. */ @@ -20,15 +20,15 @@ class Dependency : IDependency; class Service( IDependency dep1, IDependency dep2, - Lazy<(IDependency dep3, IDependency dep4)> deps) + (IDependency dep3, IDependency dep4) deps) { public IDependency Dep1 { get; } = dep1; public IDependency Dep2 { get; } = dep2; - public IDependency Dep3 { get; } = deps.Value.dep3; + public IDependency Dep3 { get; } = deps.dep3; - public IDependency Dep4 { get; } = deps.Value.dep4; + public IDependency Dep4 { get; } = deps.dep4; } // } @@ -42,6 +42,7 @@ public void Run() // This hint indicates to not generate methods such as Resolve .Hint(Hint.Resolve, "Off") .Bind().As(Lifetime.PerBlock).To() + .Bind().As(Lifetime.Singleton).To<(IDependency dep3, IDependency dep4)>() // Composition root .Root("Root"); diff --git a/tests/Pure.DI.UsageTests/Lifetimes/PerResolveScenario.cs b/tests/Pure.DI.UsageTests/Lifetimes/PerResolveScenario.cs index 1539b171f..5dbf223f6 100644 --- a/tests/Pure.DI.UsageTests/Lifetimes/PerResolveScenario.cs +++ b/tests/Pure.DI.UsageTests/Lifetimes/PerResolveScenario.cs @@ -1,6 +1,6 @@ /* $v=true -$p=1 +$p=2 $d=PerResolve $h=The _PerResolve_ lifetime guarantees that there will be a single instance of the dependency for each root of the composition. */ @@ -20,15 +20,15 @@ class Dependency : IDependency; class Service( IDependency dep1, IDependency dep2, - Lazy<(IDependency dep3, IDependency dep4)> deps) + (IDependency dep3, IDependency dep4) deps) { public IDependency Dep1 { get; } = dep1; public IDependency Dep2 { get; } = dep2; - public IDependency Dep3 { get; } = deps.Value.dep3; + public IDependency Dep3 { get; } = deps.dep3; - public IDependency Dep4 { get; } = deps.Value.dep4; + public IDependency Dep4 { get; } = deps.dep4; } // } @@ -42,6 +42,7 @@ public void Run() // This hint indicates to not generate methods such as Resolve .Hint(Hint.Resolve, "Off") .Bind().As(Lifetime.PerResolve).To() + .Bind().As(Lifetime.Singleton).To<(IDependency dep3, IDependency dep4)>() // Composition root .Root("Root"); diff --git a/tests/Pure.DI.UsageTests/Lifetimes/ScopeScenario.cs b/tests/Pure.DI.UsageTests/Lifetimes/ScopeScenario.cs index 76d5fd292..674e3fce6 100644 --- a/tests/Pure.DI.UsageTests/Lifetimes/ScopeScenario.cs +++ b/tests/Pure.DI.UsageTests/Lifetimes/ScopeScenario.cs @@ -1,6 +1,6 @@ /* $v=true -$p=1 +$p=4 $d=Scope $h=The _Scoped_ lifetime ensures that there will be a single instance of the dependency for each scope. */ diff --git a/tests/Pure.DI.UsageTests/Lifetimes/SingletonScenario.cs b/tests/Pure.DI.UsageTests/Lifetimes/SingletonScenario.cs index 1af141880..0b8dd1d83 100644 --- a/tests/Pure.DI.UsageTests/Lifetimes/SingletonScenario.cs +++ b/tests/Pure.DI.UsageTests/Lifetimes/SingletonScenario.cs @@ -1,6 +1,6 @@ /* $v=true -$p=0 +$p=1 $d=Singleton $h=The _Singleton_ lifetime ensures that there will be a single instance of the dependency for each composition. */ diff --git a/tests/Pure.DI.UsageTests/Lifetimes/TransientScenario.cs b/tests/Pure.DI.UsageTests/Lifetimes/TransientScenario.cs index b9b47ef5c..d4a1ee52f 100644 --- a/tests/Pure.DI.UsageTests/Lifetimes/TransientScenario.cs +++ b/tests/Pure.DI.UsageTests/Lifetimes/TransientScenario.cs @@ -1,6 +1,6 @@ /* $v=true -$p=2 +$p=0 $d=Transient $h=The _Transient _ lifetime specifies to create a new dependency instance each time. It is the default lifetime and can be omitted. */