Skip to content

Commit

Permalink
ActivatorUtilities.CreateInstance() should respect [ActivatorUtilitie…
Browse files Browse the repository at this point in the history
…sConstructor] (dotnet#99175)
  • Loading branch information
steveharter authored Mar 6, 2024
1 parent 79a1c91 commit bb919c2
Show file tree
Hide file tree
Showing 2 changed files with 204 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -103,20 +103,25 @@ public static object CreateInstance(
{
ThrowMarkedCtorDoesNotTakeAllProvidedArguments();
}
}

if (isPreferred || bestLength < length)
{
seenPreferred = true;
bestLength = length;
bestMatcher = matcher;
multipleBestLengthFound = false;
}
else if (bestLength == length)
else if (!seenPreferred)
{
multipleBestLengthFound = true;
if (bestLength < length)
{
bestLength = length;
bestMatcher = matcher;
multipleBestLengthFound = false;
}
else if (bestLength == length)
{
multipleBestLengthFound = true;
}
}

seenPreferred |= isPreferred;
}

if (bestLength != -1)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,96 @@ public void CreateInstance_ClassWithABC_ConstructorWithAttribute_PicksCtorWithAt
Assert.Same(a, instance.A);
}

[Fact]
public void CreateInstanceFailsWithAmbiguousConstructor()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<ClassWithA_And_B>();
serviceCollection.AddTransient<A>();
serviceCollection.AddTransient<B>();

var serviceProvider = serviceCollection.BuildServiceProvider();

// Neither ctor(A) nor ctor(B) have [ActivatorUtilitiesConstructor].
Assert.Throws<InvalidOperationException>(() => ActivatorUtilities.CreateInstance<ClassWithA_And_B>(serviceProvider));
}

[Fact]
public void CreateInstanceFailsWithAmbiguousConstructor_ReversedOrder()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<ClassWithB_And_A>();
serviceCollection.AddTransient<A>();
serviceCollection.AddTransient<B>();

var serviceProvider = serviceCollection.BuildServiceProvider();

// Neither ctor(A) nor ctor(B) have [ActivatorUtilitiesConstructor].
Assert.Throws<InvalidOperationException>(() => ActivatorUtilities.CreateInstance<ClassWithA_And_B>(serviceProvider));
}

[Fact]
public void CreateInstancePassesWithAmbiguousConstructor()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<ClassWithA_And_B_ActivatorUtilitiesConstructorAttribute>();
serviceCollection.AddTransient<A>();
serviceCollection.AddTransient<B>();

var serviceProvider = serviceCollection.BuildServiceProvider();
var service = ActivatorUtilities.CreateInstance<ClassWithA_And_B_ActivatorUtilitiesConstructorAttribute>(serviceProvider);

// Ensure ctor(A) was selected over ctor(B) since A has [ActivatorUtilitiesConstructor].
Assert.NotNull(service.A);
}

[Fact]
public void CreateInstancePassesWithAmbiguousConstructor_ReversedOrder()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<ClassWithB_And_A_ActivatorUtilitiesConstructorAttribute>();
serviceCollection.AddTransient<A>();
serviceCollection.AddTransient<B>();

var serviceProvider = serviceCollection.BuildServiceProvider();
var service = ActivatorUtilities.CreateInstance<ClassWithB_And_A_ActivatorUtilitiesConstructorAttribute>(serviceProvider);

// Ensure ctor(A) was selected over ctor(B) since A has [ActivatorUtilitiesConstructor].
Assert.NotNull(service.A);
}

[Fact]
public void CreateInstanceIgnoresActivatorUtilitiesConstructorAttribute()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<ClassWithA_And_AB_ActivatorUtilitiesConstructorAttribute>();
serviceCollection.AddTransient<A>();
serviceCollection.AddTransient<B>();

var serviceProvider = serviceCollection.BuildServiceProvider();
var service = ActivatorUtilities.CreateInstance<ClassWithA_And_AB_ActivatorUtilitiesConstructorAttribute>(serviceProvider);

// Ensure ctor(A) was selected since A has [ActivatorUtilitiesConstructor].
Assert.NotNull(service.A);
Assert.Null(service.B);
}

[Fact]
public void CreateInstanceIgnoresActivatorUtilitiesConstructorAttribute_ReversedOrder()
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<ClassWithAB_And_A_ActivatorUtilitiesConstructorAttribute>();
serviceCollection.AddTransient<A>();
serviceCollection.AddTransient<B>();

var serviceProvider = serviceCollection.BuildServiceProvider();
var service = ActivatorUtilities.CreateInstance<ClassWithAB_And_A_ActivatorUtilitiesConstructorAttribute>(serviceProvider);

// Ensure ctor(A) was selected since it has [ActivatorUtilitiesConstructor].
Assert.NotNull(service.A);
Assert.Null(service.B);
}

[Fact]
public void CreateInstance_ClassWithABC_MultipleCtorsWithSameLength_ThrowsAmbiguous()
{
Expand Down Expand Up @@ -662,6 +752,108 @@ public ClassWithABC_LastConstructorWithAttribute(B b, C c) : this(null, b, c) {
public ClassWithABC_LastConstructorWithAttribute(A a, B b, C c) : base(a, b, c) { }
}

internal class ClassWithA_And_B
{
public ClassWithA_And_B(A a)
{
A = a;
}

public ClassWithA_And_B(B b)
{
B = b;
}

public A A { get; }
public B B { get; }
}

internal class ClassWithB_And_A
{
public ClassWithB_And_A(A a)
{
A = a;
}

public ClassWithB_And_A(B b)
{
B = b;
}

public A A { get; }
public B B { get; }
}

internal class ClassWithA_And_B_ActivatorUtilitiesConstructorAttribute
{
[ActivatorUtilitiesConstructor]
public ClassWithA_And_B_ActivatorUtilitiesConstructorAttribute(A a)
{
A = a;
}

public ClassWithA_And_B_ActivatorUtilitiesConstructorAttribute(B b)
{
B = b;
}

public A A { get; }
public B B { get; }
}

internal class ClassWithB_And_A_ActivatorUtilitiesConstructorAttribute
{
public ClassWithB_And_A_ActivatorUtilitiesConstructorAttribute(B b)
{
B = b;
}

[ActivatorUtilitiesConstructor]
public ClassWithB_And_A_ActivatorUtilitiesConstructorAttribute(A a)
{
A = a;
}

public A A { get; }
public B B { get; }
}

internal class ClassWithA_And_AB_ActivatorUtilitiesConstructorAttribute
{
[ActivatorUtilitiesConstructor]
public ClassWithA_And_AB_ActivatorUtilitiesConstructorAttribute(A a)
{
A = a;
}

public ClassWithA_And_AB_ActivatorUtilitiesConstructorAttribute(A a, B b)
{
A = a;
B = b;
}

public A A { get; }
public B B { get; }
}

internal class ClassWithAB_And_A_ActivatorUtilitiesConstructorAttribute
{
public ClassWithAB_And_A_ActivatorUtilitiesConstructorAttribute(A a, B b)
{
A = a;
B = b;
}

[ActivatorUtilitiesConstructor]
public ClassWithAB_And_A_ActivatorUtilitiesConstructorAttribute(A a)
{
A = a;
}

public A A { get; }
public B B { get; }
}

internal class FakeServiceProvider : IServiceProvider
{
private IServiceProvider _inner;
Expand Down

0 comments on commit bb919c2

Please sign in to comment.