Home > Net >  Run-time generated TResult for Func<T,TResult>
Run-time generated TResult for Func<T,TResult>

Time:06-07

There is an external generic method, which I call with reflection. But there is a trick: this method has a following signature – Foo<TResult>(Func<Bar, TResult> factory). The problem is that in my method, I get TResult not as generic type, but dynamically, during run-time. Actually, there is a whole purpose of my method – being dynamic. I tried following code as parameter for my reflected Foo:

Func<Bar, dynamic> factory = (bar) =>
{
    return tResultInstance;
};

Yes, I actually don't need Bar, it is not a mistake. :) Problem is that when I invoking my reflected Foo it fails with casting exception, because it cannot cast System.Object to TResult.

So, is that possible to dynamically (during run-time) create such Func to pass it to dynamical invocation?

UPD 0.

Static.

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<Baz>(_ => new Baz("string"));

Dynamic.

private const string AddScopedMethodName = "AddScoped";
private const string AddScopedMethodSignature = "Microsoft.Extensions.DependencyInjection.IServiceCollection AddScoped[TService](Microsoft.Extensions.DependencyInjection.IServiceCollection, System.Func`2[System.IServiceProvider,TService])";

<...>

var bazConstructorInfo = bazType.GetConstructor(new Type[1] // bazType is Baz.
{
    typeof(String)
});
var bazConstructorParameters = new object[1]
{
    inputString // inputString is "string".
};
var bazInstance = bazConstructorInfo.Invoke(bazConstructorParameters);

var addScopedMethodInfo = typeof(ServiceCollectionServiceExtensions)
    .GetMethods()
    .Where(method =>
        method.IsPublic &&
        method.IsGenericMethod &&
        (method.Name == AddScopedMethodName) &&
        (method.ToString() == AddScopedMethodSignature))
    .Single();
var addScopedMethod = addScopedMethodInfo.MakeGenericMethod(bazType);
Func<IServiceProvider, dynamic> implementationFactory = (serviceProvider) =>
{
    return bazInstance;
};
var addScopedMethodParameters = new object[2]
{
    services, // services is IServiceCollection.
    implementationFactory
};
addScopedMethod.Invoke(
    services,
    addScopedMethodParameters); // Fails with "Object of type 'System.Func`2[System.IServiceProvider,System.Object]' cannot be converted to type 'System.Func`2[System.IServiceProvider,Baz]'." exception. :(

UPD 1.

To @alexei-levenkov. It looks like Runtime creation of generic Func<T> is not answer to me. They are using following code:

static Foo CreateFoo() { return new Foo(); }

<...>

var method = typeof(Program)
    .GetMethod(
        "CreateFoo",
        BindingFlags.NonPublic | BindingFlags.Static);

But in my case, I do not know Foo before runtime. I just cannot write this static Foo CreateFoo() { return new Foo(); } line.

CodePudding user response:

A quick fix would be adding helper class:

class Helper
{
    public static Func<IServiceProvider, T> GetFactory<T>(T instance) => _ => instance;
}

And using it to get implementation factory:

var methodInfo = typeof(Helper).GetMethod(nameof(Helper.GetFactory));
var implementationFactory = methodInfo.MakeGenericMethod(bazType).Invoke(null, new[] { bazInstance });

But in general all of this seems a huge overkill when you can just use ServiceDescriptor directly:

Func<IServiceProvider, object> implementationFactory = _ => bazInstance;
var serviceDescriptor = new ServiceDescriptor(bazType, implementationFactory, ServiceLifetime.Scoped);
services.Add(serviceDescriptor);

Also the instance creation reflection can be reduced to Activator.CreateInstance call:

var services = new ServiceCollection();
Type bazType = typeof(Baz);
var bazConstructorParameters = new object[]
{
    "string" // inputString is "string".
};
var bazInstance = Activator.CreateInstance(bazType, bazConstructorParameters);
Func<IServiceProvider, object> implementationFactory = _ => bazInstance;
var serviceDescriptor = new ServiceDescriptor(bazType, implementationFactory, ServiceLifetime.Scoped);
services.Add(serviceDescriptor);
  • Related