Description
Description
Generic collections that have copy constructors are initialized with elements, when using ActivatorUtilities.CreateInstance
/ActivatorUtilities.GetServiceOrCreateInstance
.
Example:
public interface ITest { }
public class Test : ITest { }
public class Test2 : ITest { }
public static void Main(string[] args)
{
var serviceCollection = new ServiceCollection();
serviceCollection.AddTransient<ITest, Test>();
serviceCollection.AddTransient<ITest, Test2>();
var services = serviceCollection.BuildServiceProvider();
var collection = ActivatorUtilities.CreateInstance<ObservableCollection<ITest>>(services);
}
Version
.NET 8 GA
Previous behavior
The collection
object is an empty instance of ObservableCollection
.
ActivatorUtilities.CreateInstance
used the constructor public ObservableCollection();
New behavior
The collection
object is an instance of ObservableCollection
with two elements (an instance of Test
and another of Test2
).
ActivatorUtilities.CreateInstance
is now choosing the constructor public ObservableCollection(IEnumerable<T> collection);
Type of breaking change
- Binary incompatible: Existing binaries may encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
- Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code may require source changes to compile successfully.
- Behavioral change: Existing binaries may behave differently at run time.
Reason for change
It should be related with:
- Extensions breaking changes Extensions breaking changes #33714
- [Breaking change]: ActivatorUtilities.CreateInstance behaves consistently independent of ctor definition order [Breaking change]:
ActivatorUtilities.CreateInstance
behaves consistently independent of ctor definition order #31785
Currently, ActivatorUtilities.CreateInstance
is choosing the constructor with most parameters that can be resolved from IServiceProviderIsService
(since I'm not given any parameter to be used on the constructor), while previously it would pick the constructor with most parameters that could be resolved from the given ones (choosing the parameteless constructor, in this example).
Recommended action
I'm not sure on the workaround.
This issue is affecting a custom JSON deserializer that uses ActivatorUtilities.GetServiceOrCreateInstance
to instantiate the needed types (some of them are .Net generic collections). I can't change the types being sent on the JSON and I know that collections must be empty after initalization (and then filled up with the elements from the JSON payload), I maybe register custom collections on the dependency injection container to replace the .Net ones (inheritance + construtor overload). Other workarounds are very welcome.
Maybe it would make sense to make sure that ActivatorUtilities.GetServiceOrCreateInstance
chooses the parameterless constructor on those cases - generic collections with copy constructors (by moving the parameteless construtor afterwards the on requesting IEnumerable<T>
and by adding the ActivatorUtilitiesConstructorAttribute
to it).
Feature area
C#, Core .NET libraries, Extensions
Affected APIs
ActivatorUtilities.CreateInstance
ActivatorUtilities.GetServiceOrCreateInstance