From 0b6488aa6f5ea5583a1a3b07ac1675356996da39 Mon Sep 17 00:00:00 2001 From: Alistair Evans Date: Thu, 31 Mar 2022 08:29:21 +0100 Subject: [PATCH] Add generic delegate lambda overloads, and associated tests --- .../RegistrationExtensions.Delegates.cs | 817 ++++++++++++++++++ .../LambdaGenericOverloadRegistrationTests.cs | 534 ++++++++++++ 2 files changed, 1351 insertions(+) create mode 100644 src/Autofac/RegistrationExtensions.Delegates.cs create mode 100644 test/Autofac.Specification.Test/Registration/LambdaGenericOverloadRegistrationTests.cs diff --git a/src/Autofac/RegistrationExtensions.Delegates.cs b/src/Autofac/RegistrationExtensions.Delegates.cs new file mode 100644 index 000000000..98944c280 --- /dev/null +++ b/src/Autofac/RegistrationExtensions.Delegates.cs @@ -0,0 +1,817 @@ +// Copyright (c) Autofac Project. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using System.Globalization; +using System.Linq.Expressions; +using System.Reflection; +using Autofac.Builder; +using Autofac.Core; +using Autofac.Core.Activators.Delegate; +using Autofac.Core.Activators.ProvidedInstance; +using Autofac.Core.Activators.Reflection; +using Autofac.Core.Lifetime; +using Autofac.Core.Resolving.Pipeline; +using Autofac.Features.Scanning; +using Autofac.Util; + +namespace Autofac; + +/// +/// Adds registration syntax to the type. +/// +[SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] +public static partial class RegistrationExtensions +{ + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c, + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c, + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c, + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c, + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c, + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TDependency6 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c, + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TDependency6 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TDependency6 : notnull + where TDependency7 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c, + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TDependency6 : notnull + where TDependency7 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TDependency6 : notnull + where TDependency7 : notnull + where TDependency8 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c, + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TDependency6 : notnull + where TDependency7 : notnull + where TDependency8 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TDependency6 : notnull + where TDependency7 : notnull + where TDependency8 : notnull + where TDependency9 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c, + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TDependency6 : notnull + where TDependency7 : notnull + where TDependency8 : notnull + where TDependency9 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TDependency6 : notnull + where TDependency7 : notnull + where TDependency8 : notnull + where TDependency9 : notnull + where TDependency10 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c, + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } + + /// + /// Register a delegate as a component. + /// + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of a dependency to inject into the delegate. + /// The type of the instance. + /// Container builder. + /// The delegate to register. + /// Registration builder allowing the registration to be configured. + public static IRegistrationBuilder + Register( + this ContainerBuilder builder, + Func @delegate) + where TDependency1 : notnull + where TDependency2 : notnull + where TDependency3 : notnull + where TDependency4 : notnull + where TDependency5 : notnull + where TDependency6 : notnull + where TDependency7 : notnull + where TDependency8 : notnull + where TDependency9 : notnull + where TDependency10 : notnull + where TComponent : notnull + { + if (@delegate == null) + { + throw new ArgumentNullException(nameof(@delegate)); + } + + return builder.Register((c, p) => @delegate( + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve(), + c.Resolve())); + } +} diff --git a/test/Autofac.Specification.Test/Registration/LambdaGenericOverloadRegistrationTests.cs b/test/Autofac.Specification.Test/Registration/LambdaGenericOverloadRegistrationTests.cs new file mode 100644 index 000000000..7a7b8edbb --- /dev/null +++ b/test/Autofac.Specification.Test/Registration/LambdaGenericOverloadRegistrationTests.cs @@ -0,0 +1,534 @@ +// Copyright (c) Autofac Project. All rights reserved. +// Licensed under the MIT License. See LICENSE in the project root for license information. + +using System.Reflection; + +namespace Autofac.Specification.Test.Registration; + +public class LambdaGenericOverloadRegistrationTests +{ + private class MyDep1 + { + } + + private class MyDep2 + { + } + + private class MyDep3 + { + } + + private class MyDep4 + { + } + + private class MyDep5 + { + } + + private class MyDep6 + { + } + + private class MyDep7 + { + } + + private class MyDep8 + { + } + + private class MyDep9 + { + } + + private class MyDep10 + { + } + + private class MyComponent + { + public MyComponent(MyDep1 d1) + { + } + + public MyComponent(MyDep1 d1, MyDep2 d2) + { + } + + public MyComponent(MyDep1 d1, MyDep2 d2, MyDep3 d3) + { + } + + public MyComponent(MyDep1 d1, MyDep2 d2, MyDep3 d3, MyDep4 d4) + { + } + + public MyComponent(MyDep1 d1, MyDep2 d2, MyDep3 d3, MyDep4 d4, MyDep5 d5) + { + } + + public MyComponent(MyDep1 d1, MyDep2 d2, MyDep3 d3, MyDep4 d4, MyDep5 d5, MyDep6 d6) + { + } + + public MyComponent(MyDep1 d1, MyDep2 d2, MyDep3 d3, MyDep4 d4, MyDep5 d5, MyDep6 d6, MyDep7 d7) + { + } + + public MyComponent(MyDep1 d1, MyDep2 d2, MyDep3 d3, MyDep4 d4, MyDep5 d5, MyDep6 d6, MyDep7 d7, MyDep8 d8) + { + } + + public MyComponent(MyDep1 d1, MyDep2 d2, MyDep3 d3, MyDep4 d4, MyDep5 d5, MyDep6 d6, MyDep7 d7, MyDep8 d8, MyDep9 d9) + { + } + + public MyComponent(MyDep1 d1, MyDep2 d2, MyDep3 d3, MyDep4 d4, MyDep5 d5, MyDep6 d6, MyDep7 d7, MyDep8 d8, MyDep9 d9, MyDep10 d10) + { + } + } + + private static readonly Type[] _allDeps = new[] + { + typeof(MyDep1), + typeof(MyDep2), + typeof(MyDep3), + typeof(MyDep4), + typeof(MyDep5), + typeof(MyDep6), + typeof(MyDep7), + typeof(MyDep8), + typeof(MyDep9), + typeof(MyDep10), + }; + + private ContainerBuilder GetBuilderWithDeps() + { + var builder = new ContainerBuilder(); + + builder.RegisterTypes(_allDeps); + + return builder; + } + + [Fact] + public void RegisterLambdaWith1DependencyAndContext() + { + var builder = GetBuilderWithDeps(); + + builder.Register((IComponentContext c, MyDep1 dep1) => new MyComponent(dep1)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith1Dependency() + { + var builder = GetBuilderWithDeps(); + + builder.Register((MyDep1 dep1) => new MyComponent(dep1)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith2DependenciesAndContext() + { + var builder = GetBuilderWithDeps(); + + builder.Register((IComponentContext c, MyDep1 dep1, MyDep2 dep2) => new MyComponent(dep1, dep2)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith2Dependencies() + { + var builder = GetBuilderWithDeps(); + + builder.Register((MyDep1 dep1, MyDep2 dep2) => new MyComponent(dep1, dep2)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith3DependenciesAndContext() + { + var builder = GetBuilderWithDeps(); + + builder.Register((IComponentContext c, MyDep1 dep1, MyDep2 dep2, MyDep3 dep3) => new MyComponent(dep1, dep2, dep3)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith3Dependencies() + { + var builder = GetBuilderWithDeps(); + + builder.Register((MyDep1 dep1, MyDep2 dep2, MyDep3 dep3) => new MyComponent(dep1, dep2, dep3)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith4DependenciesAndContext() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + IComponentContext c, + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4) => new MyComponent(dep1, dep2, dep3, dep4)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith4Dependencies() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4) => new MyComponent(dep1, dep2, dep3, dep4)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith5DependenciesAndContext() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + IComponentContext c, + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5) => new MyComponent(dep1, dep2, dep3, dep4, dep5)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith5Dependencies() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5) => new MyComponent(dep1, dep2, dep3, dep4, dep5)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith6DependenciesAndContext() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + IComponentContext c, + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5, + MyDep6 dep6) => new MyComponent(dep1, dep2, dep3, dep4, dep5, dep6)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith6Dependencies() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5, + MyDep6 dep6) => new MyComponent(dep1, dep2, dep3, dep4, dep5, dep6)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith7DependenciesAndContext() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + IComponentContext c, + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5, + MyDep6 dep6, + MyDep7 dep7) => new MyComponent(dep1, dep2, dep3, dep4, dep5, dep6, dep7)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith7Dependencies() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5, + MyDep6 dep6, + MyDep7 dep7) => new MyComponent(dep1, dep2, dep3, dep4, dep5, dep6, dep7)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith8DependenciesAndContext() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + IComponentContext c, + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5, + MyDep6 dep6, + MyDep7 dep7, + MyDep8 dep8) => new MyComponent(dep1, dep2, dep3, dep4, dep5, dep6, dep7, dep8)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith8Dependencies() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5, + MyDep6 dep6, + MyDep7 dep7, + MyDep8 dep8) => new MyComponent(dep1, dep2, dep3, dep4, dep5, dep6, dep7, dep8)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith9DependenciesAndContext() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + IComponentContext c, + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5, + MyDep6 dep6, + MyDep7 dep7, + MyDep8 dep8, + MyDep9 dep9) => new MyComponent(dep1, dep2, dep3, dep4, dep5, dep6, dep7, dep8, dep9)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith9Dependencies() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5, + MyDep6 dep6, + MyDep7 dep7, + MyDep8 dep8, + MyDep9 dep9) => new MyComponent(dep1, dep2, dep3, dep4, dep5, dep6, dep7, dep8, dep9)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith10DependenciesAndContext() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + IComponentContext c, + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5, + MyDep6 dep6, + MyDep7 dep7, + MyDep8 dep8, + MyDep9 dep9, + MyDep10 dep10) => new MyComponent(dep1, dep2, dep3, dep4, dep5, dep6, dep7, dep8, dep9, dep10)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Fact] + public void RegisterLambdaWith10Dependencies() + { + var builder = GetBuilderWithDeps(); + + builder.Register(( + MyDep1 dep1, + MyDep2 dep2, + MyDep3 dep3, + MyDep4 dep4, + MyDep5 dep5, + MyDep6 dep6, + MyDep7 dep7, + MyDep8 dep8, + MyDep9 dep9, + MyDep10 dep10) => new MyComponent(dep1, dep2, dep3, dep4, dep5, dep6, dep7, dep8, dep9, dep10)); + + var context = builder.Build(); + + context.Resolve(); + } + + [Theory] + [MemberData(nameof(GetGenericOverloadTypeSets))] + public void OverloadsAllCheckNullDelegate(Type[] types) + { + var builder = GetBuilderWithDeps(); + + var registerMethod = GetRegisterMethod(types, withComponentContext: false); + + // Invoke generated delegate. + AssertThrowsViaInvocation(() => registerMethod.Invoke(null, new[] { builder, null })); + } + + [Theory] + [MemberData(nameof(GetGenericOverloadTypeSets))] + public void OverloadsWithContextAllCheckNullDelegate(Type[] types) + { + var builder = GetBuilderWithDeps(); + + var registerMethod = GetRegisterMethod(types, withComponentContext: true); + + // Invoke generated delegate. + AssertThrowsViaInvocation(() => registerMethod.Invoke(null, new[] { builder, null })); + } + + private void AssertThrowsViaInvocation(Action testCode) + where TException : Exception + { + Assert.Throws(() => + { + try + { + testCode(); + } + catch (TargetInvocationException ex) + { + if (ex.InnerException is not null) + { + throw ex.InnerException; + } + + throw; + } + }); + } + + /// + /// Generate a concrete register overload from the given set of types, indicating whether you want the one with a component context + /// at the start, or not. + /// + private MethodInfo GetRegisterMethod(Type[] types, bool withComponentContext) + { + static bool MethodDelegateFuncHasIComponentContext(MethodInfo method) + => method.GetParameters().Last().ParameterType.GetGenericArguments().FirstOrDefault() == typeof(IComponentContext); + + var genericMethod = typeof(RegistrationExtensions).GetMethods(BindingFlags.Static | BindingFlags.Public) + .Where(x => x.Name == nameof(RegistrationExtensions.Register) && + x.GetGenericArguments().Length == types.Length + 1 && + MethodDelegateFuncHasIComponentContext(x) == withComponentContext) + .FirstOrDefault(); + + var actualMethod = genericMethod.MakeGenericMethod(types.Append(typeof(MyComponent)).ToArray()); + + return actualMethod; + } + + public static IEnumerable GetGenericOverloadTypeSets() + { + // Return a set of type arrays, each one with an additional type in the set. + for (var idx = 0; idx < _allDeps.Length; idx++) + { + yield return new[] { _allDeps.Take(idx + 1).ToArray() }; + } + } +}