Skip to content

Commit

Permalink
Adds ActivatorUtilities.CreateFactory<T> API (#77185)
Browse files Browse the repository at this point in the history
  • Loading branch information
maryamariyan authored Oct 20, 2022
1 parent de5b185 commit e542a20
Show file tree
Hide file tree
Showing 4 changed files with 74 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ namespace Microsoft.Extensions.DependencyInjection
public static partial class ActivatorUtilities
{
public static Microsoft.Extensions.DependencyInjection.ObjectFactory CreateFactory([System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type instanceType, System.Type[] argumentTypes) { throw null; }
public static Microsoft.Extensions.DependencyInjection.ObjectFactory<T> CreateFactory<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] T>(System.Type[] argumentTypes) { throw null; }
public static object CreateInstance(System.IServiceProvider provider, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type instanceType, params object[] parameters) { throw null; }
public static T CreateInstance<[System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] T>(System.IServiceProvider provider, params object[] parameters) { throw null; }
public static object GetServiceOrCreateInstance(System.IServiceProvider provider, [System.Diagnostics.CodeAnalysis.DynamicallyAccessedMembersAttribute(System.Diagnostics.CodeAnalysis.DynamicallyAccessedMemberTypes.PublicConstructors)] System.Type type) { throw null; }
Expand Down Expand Up @@ -53,6 +54,7 @@ public partial interface ISupportRequiredService
object GetRequiredService(System.Type serviceType);
}
public delegate object ObjectFactory(System.IServiceProvider serviceProvider, object?[]? arguments);
public delegate T ObjectFactory<T>(System.IServiceProvider serviceProvider, object?[]? arguments);
public partial class ServiceCollection : Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Collections.Generic.ICollection<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.Generic.IEnumerable<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.Generic.IList<Microsoft.Extensions.DependencyInjection.ServiceDescriptor>, System.Collections.IEnumerable
{
public ServiceCollection() { }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,11 +127,7 @@ public static ObjectFactory CreateFactory(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type instanceType,
Type[] argumentTypes)
{
FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap);

ParameterExpression? provider = Expression.Parameter(typeof(IServiceProvider), "provider");
ParameterExpression? argumentArray = Expression.Parameter(typeof(object[]), "argumentArray");
Expression? factoryExpressionBody = BuildFactoryExpression(constructor, parameterMap, provider, argumentArray);
CreateFactoryInternal(instanceType, argumentTypes, out ParameterExpression provider, out ParameterExpression argumentArray, out Expression factoryExpressionBody);

var factoryLambda = Expression.Lambda<Func<IServiceProvider, object?[]?, object>>(
factoryExpressionBody, provider, argumentArray);
Expand All @@ -140,6 +136,40 @@ public static ObjectFactory CreateFactory(
return result.Invoke;
}

/// <summary>
/// Create a delegate that will instantiate a type with constructor arguments provided directly
/// and/or from an <see cref="IServiceProvider"/>.
/// </summary>
/// <typeparam name="T">The type to activate</typeparam>
/// <param name="argumentTypes">
/// The types of objects, in order, that will be passed to the returned function as its second parameter
/// </param>
/// <returns>
/// A factory that will instantiate type T using an <see cref="IServiceProvider"/>
/// and an argument array containing objects matching the types defined in argumentTypes
/// </returns>
public static ObjectFactory<T>
CreateFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
Type[] argumentTypes)
{
CreateFactoryInternal(typeof(T), argumentTypes, out ParameterExpression provider, out ParameterExpression argumentArray, out Expression factoryExpressionBody);

var factoryLambda = Expression.Lambda<Func<IServiceProvider, object?[]?, T>>(
factoryExpressionBody, provider, argumentArray);

Func<IServiceProvider, object?[]?, T>? result = factoryLambda.Compile();
return result.Invoke;
}

private static void CreateFactoryInternal([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type instanceType, Type[] argumentTypes, out ParameterExpression provider, out ParameterExpression argumentArray, out Expression factoryExpressionBody)
{
FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap);

provider = Expression.Parameter(typeof(IServiceProvider), "provider");
argumentArray = Expression.Parameter(typeof(object[]), "argumentArray");
factoryExpressionBody = BuildFactoryExpression(constructor, parameterMap, provider, argumentArray);
}

/// <summary>
/// Instantiate a type with constructor arguments provided directly and/or from an <see cref="IServiceProvider"/>.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System;

namespace Microsoft.Extensions.DependencyInjection
{
/// <summary>
/// The result of <see cref="ActivatorUtilities.CreateFactory{T}"/>. A delegate to specify a factory method to call to instantiate an instance of type `T`
/// </summary>
/// <typeparam name="T">The type of the instance being returned</typeparam>
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to get service arguments from.</param>
/// <param name="arguments">Additional constructor arguments.</param>
/// <returns>An instance of T</returns>
public delegate T ObjectFactory<T>(IServiceProvider serviceProvider, object?[]? arguments);
}
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,27 @@ public void CreateInstance_ClassWithABC_MultipleCtorsWithSameLength_ThrowsAmbigu
ActivatorUtilities.CreateInstance<ClassWithABC_MultipleCtorsWithSameLength>(provider));
Assert.Equal(message, exception.Message);
}

[Fact]
public void CreateFactory_CreatesFactoryMethod()
{
var factory1 = ActivatorUtilities.CreateFactory(typeof(ClassWithABCS), new Type[] { typeof(B) });
var factory2 = ActivatorUtilities.CreateFactory<ClassWithABCS>(new Type[] { typeof(B) });

var services = new ServiceCollection();
services.AddSingleton(new A());
services.AddSingleton(new C());
services.AddSingleton(new S());
using var provider = services.BuildServiceProvider();
object item1 = factory1(provider, new[] { new B() });
var item2 = factory2(provider, new[] { new B() });

Assert.IsType<ObjectFactory>(factory1);
Assert.IsType<ClassWithABCS>(item1);

Assert.IsType<ObjectFactory<ClassWithABCS>>(factory2);
Assert.IsType<ClassWithABCS>(item2);
}
}

internal class A { }
Expand Down

0 comments on commit e542a20

Please sign in to comment.