Description
Background and motivation
To construct a object of type T
from a parameterless constructor, we have a few API options today. If we know that the constructor is public, we can use Activator.CreateInstance<T>()
and Activator.CreateInstance(Type)
. If the constructor may be non-public, we only have a non-generic Activator.CreateInstance(Type, bool)
method.
For our SafeHandleMarshaller
type that we use in the interop source generators, we need to support non-public constructors for backward-compatibility reasons. As a result, we need to use Activator.CreateInstance(Type, bool)
to optionally support a non-public default constructor.
CoreCLR accelerates both Activator.CreateInstance<T>()
and Activator.CreateInstance(Type, bool)
to be almost as fast as writing new T()
with a specific T
, so adding a new generic Activator.CreateInstance
method to support constructing an object from a non-public constructor isn't necessary.
NativeAOT has a very powerful optimization path for Activator.CreateInstance<T>()
to avoid pulling in the reflection stack. It does not have this optimization for Activator.CreateInstance(Type, bool)
, and it cannot have this optimization in a non-generic context. As a result, the new SafeHandleMarshaller
type roots the reflection stack, increasing the size of any apps that use it by quite a bit. Since LibraryImport
s with SafeHandle
s are used in the code for basically every application type (Web, Console, UI), we end up always rooting the reflection stack. If we add this API, we can implement an intrinsic implementation on NativeAOT that does not root the reflection stack and avoids the size regression.
cc: @dotnet/ilc-contrib
API Proposal
namespace System;
public static class Activator
{
+ public void CreateInstance<T>(bool nonPublic);
}
API Usage
T instance = Activator.CreateInstance<T>(nonPublic: true);
Alternative Designs
We can just have this method as internal
in System.Private.CoreLib to avoid expanding the public API surface of Activator
, as the SafeHandleMarshaller type will live in CoreLib.
We could also introduce a custom intrinsic in NativeAOT's CoreLib that the SafeHandleMarshaller
could use on NativeAOT.
Risks
No response