简体   繁体   中英

Create Func<Type> from TypeInfo and MethodInfo

I'm curious whether it's possible to create a delegate while having only the type at hand. Something like this:

var concreteType = DiscoverTypeInRuntime();
var methodName = "SomeNameIKnowInAdvance";
var methodInfo = concreteType.GetMethodInfo(methodName);
var dynamicallyConstructedFunc = DynamicallyConstructFunc(methodInfo)

The delegate should be of type

Func<ConcreteType>

UPDATED:

The exact type is not known in advance. It is discovered during the program run time. The delegate can't be of type Func<object only the Func<ConcreteType> is allowed. The method whose name is known is the factory method which returns an instance of ConcreteType thus the requirement.

UPDATED 2:

I will provide here some code to explain the use case. Hope this will clarify the question:

public class LoginProviderBuilder : FakeBuilderBase<ILoginProvider>
{        
    private readonly Dictionary<string, string> _users = new Dictionary<string, string>();

    private LoginProviderBuilder()
    {

    }
    //...
}

public class Module : ProvidersModuleBase
{
    protected override void OnRegisterProviders(IIocContainerRegistrator iocContainer)
    {
        base.OnRegisterProviders(iocContainer);
        RegisterAllBuilders(iocContainer, LoginProviderBuilder.CreateBuilder);
        RegisterAllBuilders(iocContainer, WarehouseProviderBuilder.CreateBuilder);
        RegisterAllBuilders(iocContainer, EventsProviderBuilder.CreateBuilder);
    }
}

protected void RegisterAllBuilders<TProvider>(IIocContainerRegistrator iocContainerRegistrator, 
        Func<FakeBuilderBase<TProvider>> defaultBuilderCreationFunc) where TProvider : class
    {
        var builders = BuildersCollectionContext.GetBuilders<TProvider>().ToArray();
        if (builders.Length == 0)
        {
            RegistrationHelper.RegisterBuilder(iocContainerRegistrator, defaultBuilderCreationFunc());
        }
        else
        {
            foreach (var builder in builders)
            {
                RegistrationHelper.RegisterBuilder(iocContainerRegistrator, builder);
            }
        }
    }

In short the task in hand is to discover all eligible builder type dynamically and register them automagically while somehow preserving the concrete type and the existing generics API.

If this code is not clear you can find the sample solution here: https://github.com/LogoFX/Samples.Specifications

With the help of some Reflection you can construct a function that does that like this:

private Func<T> GetDelegateFromMethodName<T>(string methodName)
{
    var type = typeof(T);
    var method = type.GetMethods().FirstOrDefault(m => m.Name == methodName);
    if (method == null)
    {
        throw new ArgumentException(nameof(methodName));
    }
    return (Func<T>) Delegate.CreateDelegate(typeof(Func<T>), method);
}

Here is an example usage:

var methodName = "SomeNameIKnowInAdvance";
var dynamicallyConstructedFunc = GetDelegateFromMethodName<ConcreteClass>(methodName);

Keep in mind this will only work for static methods, if you want to make it work for instance methods too you need to pass an instance as well:

private Func<T> GetDelegateFromMethodName<T>(T instance, string methodName)
{
    var type = typeof(T);
    var method = type.GetMethods().FirstOrDefault(m => m.Name == methodName);
    if (method == null)
    {
        throw new ArgumentException(nameof(methodName));
    }
    return (Func<T>) Delegate.CreateDelegate(typeof(Func<T>), instance, method);
}

An example usage of that would be:

var methodName = "ConstructClass";
var dynamicallyConstructedFunc = GetDelegateFromMethodName<ConcreteClass>(new ConcreteClass(), methodName);

If you don't like generics you can use object too but that wont result in Func<ConcreteClass> you will have to cast it manually:

private Func<object> GetDelegateFromMethodName(object instance, string methodName)
{
    var type = instance.GetType();
    var method = type.GetMethods().FirstOrDefault(m => m.Name == methodName);
    if (method == null)
    {
        throw new ArgumentException(nameof(methodName));
    }
    return (Func<object>) Delegate.CreateDelegate(typeof(Func<object>), instance, method);
}

private Func<object> GetDelegateFromMethodName(Type type, string methodName)
{
    var method = type.GetMethods().FirstOrDefault(m => m.Name == methodName);
    if (method == null)
    {
        throw new ArgumentException(nameof(methodName));
    }
    return (Func<object>)Delegate.CreateDelegate(typeof(Func<object>), method);
}

You can create such a delegate - but you'll only be able to refer to it as a Delegate because you don't know the actual type at compile-time. It requires creating the appropriate delegate type from Func<T> using MakeGenericType .

var concreteType = DiscoverTypeInRuntime();
var methodName = "SomeNameIKnowInAdvance";
var methodInfo = concreteType.GetMethodInfo(methodName);
var funcType = typeof(Func<>).MakeGenericType(concreteType);
var func = Delegate.CreateDelegate(funcType, methodInfo)

That will create the right kind of delegate, referring to the right method... but the compile-time type will still just be Delegate .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM