DapperAOT
DapperAOT copied to clipboard
Support non-public constructors
We already support annotated custom constructors when they're accessible to the generator. We can extend that.
Note: only impacts private
and protected
constructor usage; all others should continue using direct
This can be implemented acceptably using reflection (ideally optimized via Expression
) or [UnsafeAccessor]
(net8+ only); example
using System;
using System.Linq.Expressions;
using System.Reflection;
using System.Runtime.CompilerServices;
static class P {
static void Main()
{
var obj = CreateBar(42);
Console.WriteLine(obj.A);
obj = CreateBar(96);
Console.WriteLine(obj.A);
}
#if NET8_0_OR_GREATER
[UnsafeAccessor(UnsafeAccessorKind.Constructor)]
static extern Bar CreateBar(int a);
#else
private static Func<int, Bar>? s_CreateBar;
static Bar CreateBar(int a) => SomeUtilityHelper.GetConstructor(ref s_CreateBar)(a);
#endif
}
static class SomeUtilityHelper // in DapperAOT - maybe in RowFactory?
{
public static TDelegate GetConstructor<TDelegate>(ref TDelegate? field) where TDelegate : Delegate
{
return field ?? SlowCreate(ref field);
static TDelegate SlowCreate(ref TDelegate? field)
{
var signature = typeof(TDelegate).GetMethod(nameof(Action.Invoke));
if (signature?.ReturnType is null || signature.ReturnType == typeof(void))
{
throw new InvalidOperationException("No target-type found");
}
var methodArgs = signature.GetParameters();
var argTypes = Array.ConvertAll(methodArgs, p => p.ParameterType);
var ctor = signature.ReturnType.GetConstructor(
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, argTypes);
if (ctor is null)
{
throw new InvalidOperationException("No suitable constructor found matching "
+ string.Join<Type>(", ", argTypes));
}
var args = Array.ConvertAll(methodArgs, p => Expression.Parameter(p.ParameterType, p.Name));
field = Expression.Lambda<TDelegate>(Expression.New(ctor, args), args).Compile();
return field;
}
}
}
class Bar
{
private readonly int x;
public int A => x;
private Bar(int a) => x = a;
}