Построить дерево выражений для вызова функции с 1 или 2 параметрами - PullRequest
0 голосов
/ 31 мая 2018

Итак, немного поэкспериментируем с деревьями выражений.Вот идея: я хочу вернуть объект Func<long, byte?, object>.В зависимости от типа, для которого я хочу использовать функцию, у меня будет один из двух методов: LoadById(long, byte?) или LoadByID(long).Оба возвращают объект.Поэтому я пытаюсь сделать следующее: в зависимости от того, реализует ли тип определенный интерфейс, я использую либо classToUseFunctionOn.LoadById(long, byte?), либо classToUseFunctionOn.LoadByID(long).Итак, в основном я хочу получить следующий код, если он реализует интерфейс: (длинный идентификатор, байт? Параметры) => новый TestFacade (). LoadById (идентификатор, параметры) и (длинный идентификатор, байт? Параметры) => новыйTestFacade (). LoadByID (идентификатор).Я просто не уверен, как это сделать.Это идет не так в последние несколько строк.Лямбда-вызов указывает, что количество параметров неверно.Ниже приведен код, который у меня есть:



    private static Func GetDataExtractorForTypeWithId(Type type)
    {
        var paramId = Expression.Parameter(typeof(long), "id");
        ParameterExpression paramOptions = null;
        //gets the ConstructorInfo for the constructor of type T with a single parameter of type IDataReader
        var facadetype = GetFacadeType(type.Name);
        MethodInfo loadMethod;
        var linkedEntitiesInterface = facadetype.GetInterface(typeof(IFacadeLoadLinkedEntities).Name);
        var lamdaParameterExpressions = new List() { paramId };
        if (linkedEntitiesInterface != null)
        {
            loadMethod = facadetype.GetMethod("LoadById");
            paramOptions = Expression.Parameter(linkedEntitiesInterface.GetGenericTypeDefinition().GenericTypeArguments[1], "options");
            lamdaParameterExpressions.Add(paramOptions);
        }
        else
        {
            paramOptions = Expression.Parameter(typeof(byte?));
            loadMethod = facadetype.GetMethod("LoadByID", new Type[1]{typeof(long)});
        }
        var facadeConstructor = facadetype.GetConstructor(new Type[0]);
        var newFacade = Expression.New(facadeConstructor);
        var callLoad = Expression.Call(newFacade, loadMethod, lamdaParameterExpressions);
        lamdaParameterExpressions.Add(paramOptions);
        var returnValue = Expression.Parameter(typeof(object));
        lamdaParameterExpressions.Add(returnValue);
        var entityVariable = Expression.Variable(typeof(object), "entity");
        Expression.Assign(entityVariable, callLoad);
        var lambda = Expression.Lambda>(
            entityVariable, lamdaParameterExpressions.ToArray());
        //compiles the Expression to a usable delegete.
        return lambda.Compile();
    }

1 Ответ

0 голосов
/ 05 июня 2018

А пока я нашел способ заставить его работать:



    private static Func GetDataExtractorForTypeWithId(Type type)
            {
                var paramId = Expression.Parameter(typeof(long), "id");
                ParameterExpression paramOptions = null;
                var facadetype = GetFacadeType(type.Name) ?? typeof(AzzFacade).MakeGenericType(type);
                MethodInfo loadMethod;
                var linkedEntitiesInterface = facadetype.GetInterface(typeof(IFacadeLoadLinkedEntities).Name);
                if (linkedEntitiesInterface != null)
                {
                    loadMethod = facadetype.GetMethod("LoadById");
                    paramOptions = Expression.Parameter(typeof(byte), "options");
                }
                else
                {
                    paramOptions = Expression.Parameter(typeof(byte));
                    loadMethod = facadetype.GetMethod("LoadByID", new Type[1]{typeof(long)});
                }
                var facadeConstructor = facadetype.GetConstructor(new Type[0]);
                if(facadeConstructor==null)
                    throw new NullReferenceException($"No parameterless constructor found for facade for type {type.Name}");
                MethodCallExpression callLoad;
                var newFacade = Expression.New(facadeConstructor);
                if (linkedEntitiesInterface != null)
                {
                    var conversionExpression = Expression.Convert(paramOptions, linkedEntitiesInterface.GetGenericArguments()[1]);
                    // ReSharper disable once AssignNullToNotNullAttribute
                    callLoad = Expression.Call(newFacade, loadMethod, paramId, conversionExpression);
                }
                else
                {
                    // ReSharper disable once AssignNullToNotNullAttribute
                    callLoad = Expression.Call(newFacade, loadMethod, paramId);
                }

                var lambda = Expression.Lambda>(
                    // ReSharper disable once AssignNullToNotNullAttribute
                    callLoad, paramId, paramOptions);
                //compiles the Expression to a usable delegate.
                return lambda.Compile();
            }

...