Как вызвать метод, которому нужен Func <T>с аргументом лямбда-выражения - PullRequest
0 голосов
/ 09 февраля 2019

Я знаю, что могу вызвать универсальный метод, который напрямую принимает лямбда-выражение.
Например, в моем коде ниже я могу вызвать этот метод напрямую, но это не даст того, что мне нужно: GetAllItemsFromTableAsync (x => x.ID = 3)

Я хотел бы передать обобщенный метод в качестве делегата, и я хотел бы передать этому методу (или его производным) аргумент лямбда-выражения (фактически, фильтр базы данных), который в конечном итоге будетвызовите метод GetAllItemsFromTableAsync, предоставив необходимое выражение фильтра.

Можно ли передать метод в качестве делегата и предоставить лямбда-выражение для того же метода, когда он принимает такой как параметр?В моем коде вы увидите две примерные области, одна из которых работает в настоящее время (хотя она очень сильно упрощена по сравнению с исходным кодом, я не скомпилировал ее, но думаю, что она передает основную идею), другая пометка «не работает» - это то, что я хотел бынравится достигать тем или иным способом, если это даже возможно.В настоящее время это не так, так как я не могу понять, как ссылаться на аргумент, переданный для последовательного подключения его к другому методу.Можно ли это сделать?

Я немного новичок в лямбде и, честно говоря, все еще пытаюсь обернуть голову вокруг всего этого.Любая помощь будет принята с благодарностью.

           #region This model does work currently (overly simplified for example purposes)
    /// <summary>
    /// Loads the various controls on the page that need special processing
    /// </summary>
    /// <returns></returns>
    protected async Task RefreshControls()
    {
        await LoadPickerAsync<Car, ViewModel<Car>, List<Car>>(
            this.GetPropertyInfo(x => x.AvailableColors),
            this.GetPropertyInfo(y => y.ColorIndex),
            this.GetPropertyInfo(z => z.Color),
            LoadFromDBAsync<Colors, List<Colors>>);

        await LoadPickerAsync<Truck, ViewModel<Truck>, List<Truck>>(
            this.GetPropertyInfo(x => x.AvailableColors),
            this.GetPropertyInfo(y => y.ColorIndex),
            this.GetPropertyInfo(z => z.Color),
            LoadFromDBAsync<Colors, List<Colors>>);
    }

    /// <summary>
    /// This method is first stop in the load process.  It sorts out the properties and then calls the 
    /// delegate method (in this example LoadFromDBAsync) to perform the actual loading of the picker list.
    /// </summary>
    /// <typeparam name="TColItemType"></typeparam>
    /// <typeparam name="TViewModel"></typeparam>
    /// <typeparam name="TCollection"></typeparam>
    /// <param name="collectionProperty"></param>
    /// <param name="indexProperty"></param>
    /// <param name="objectProperty"></param>
    /// <param name="collectionLoaderAsync"></param>
    /// <returns></returns>
    protected virtual async Task<TCollection> LoadPickerAsync<TColItemType, TViewModel, TCollection>(
            PropertyInfo collectionProperty,
            PropertyInfo indexProperty,
            PropertyInfo objectProperty,
            Func<Task<TCollection>> collectionLoaderAsync) 
        //Note I need to know how to pass a filter to the method here such as "x => x.ID = 2"
                where TCollection : List<TColItemType>
    {
        //Do some special stuff with the properties

        //Actually Load the picker
        if (await collectionLoaderAsync() != default(TCollection))
        {
            //do something useful, set a flag etc...
        }
    }


    /// <summary>
    /// This method would be within each view model and may be totally different from one view
    /// model to the next and even some controls may use their own method as long as the 
    /// basic signature remains the same
    /// </summary>
    /// <typeparam name="TColItemType"></typeparam>
    /// <typeparam name="TCollection"></typeparam>
    /// <returns></returns>
    protected virtual async Task<TCollection> LoadFromDBAsync<TColItemType, TCollection>()
        //I'd like to add a way for this method to pass a filter: "Expression<Func<TColItemType, bool>> filter = null"
            where TCollection : List<TColItemType>, new()
            where TColItemType : new()
    {
        //if I add the argument parameter to the method I can not figure out how to make the call 
        //to this method in LoadPickerAsync method above
        //Expression<Func<T, bool>> filter = null,

        //do some more stuff with the loading of the items into the picker

        //Call the method to load the data and return
        //note that "fitler" is only applicable if I can add the parameter above
        return (TCollection)await GetAllItemsFromTableAsync<TColItemType>(); 
    }


    /// <summary>
    /// Gets all data from the database table with the specified filter
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="filter"></param>
    /// <param name="cancelToken"></param>
    /// <returns></returns>
    protected async Task<List<TColItemType>> GetAllItemsFromTableAsync<TColItemType>() where TColItemType : new()
    {
        //Do some prep work and null checks before calling the actual database method
        //

        //Call the database method to obtain the data
        return await database.GetAllWithChildrenAsync<TColItemType>(recursive: true);
    }
    #endregion This model does work currently (overly simplified for example purposes)


    #region Does NOT work
    /// <summary>
    /// Loads the various controls on the page that need special processing
    /// </summary>
    /// <returns></returns>
    protected async Task RefreshControls()
    {
        await LoadPickerAsync<Car, ViewModel<Car>, List<Car>>(
            this.GetPropertyInfo(x => x.AvailableColors),
            this.GetPropertyInfo(y => y.ColorIndex),
            this.GetPropertyInfo(z => z.Color),
            LoadFromDBAsync<Colors, List<Colors>>(x => x.ID == 4));

        await LoadPickerAsync<Truck, ViewModel<Truck>, List<Truck>>(
            this.GetPropertyInfo(x => x.AvailableColors),
            this.GetPropertyInfo(y => y.ColorIndex),
            this.GetPropertyInfo(z => z.Color),
            LoadFromDBAsync<Colors, List<Colors>>(x => x.ID == 3));
    }

    /// <summary>
    /// This method is first stop in the load process.  It sorts out the properties and then calls the 
    /// delegate method (in this example LoadFromDBAsync) to perform the actual loading of the picker list.
    /// </summary>
    /// <typeparam name="TColItemType"></typeparam>
    /// <typeparam name="TViewModel"></typeparam>
    /// <typeparam name="TCollection"></typeparam>
    /// <param name="collectionProperty"></param>
    /// <param name="indexProperty"></param>
    /// <param name="objectProperty"></param>
    /// <param name="collectionLoaderAsync"></param>
    /// <returns></returns>
    protected virtual async Task<TCollection> LoadPickerAsync<TColItemType, TViewModel, TCollection>(
            PropertyInfo collectionProperty,
            PropertyInfo indexProperty,
            PropertyInfo objectProperty,
            Func<Expression<Func<TColItemType, bool>>, Task<TCollection>> collectionLoaderAsync) 
            //Note I need to know how to pass a filter argument to the method here such as "x => x.ID = 2"
                where TCollection : List<TColItemType>
    {
        //Do some special stuff with the properties

        //With this overload example I can not figure out how to reference the argument passed in inorder to 
        //subsequently pass that argument when making the call to the method

        //Actually Load the picker
        await collectionLoaderAsync(filterArg);
    }


    /// <summary>
    /// This method would be within each view model and may be totally different from one view
    /// model to the next and even some controls may use their own method as long as the 
    /// basic signature remains the same
    /// </summary>
    /// <typeparam name="TColItemType"></typeparam>
    /// <typeparam name="TCollection"></typeparam>
    /// <returns></returns>
    protected virtual async Task<TCollection> LoadFromDBAsync<TColItemType, TCollection>(
        Expression<Func<TColItemType, bool>> filter = null)
        //I'd like to add a way for this method to pass a filter: "Expression<Func<TColItemType, bool>> filter = null"
            where TCollection : List<TColItemType>, new()
            where TColItemType : new()
    {
        //if I add the argument parameter to the method I can not figure out 
        //how to make the call to this method in LoadPickerAsync method above
        //Expression<Func<T, bool>> filter = null,

        //do some more stuff with the loading of the items into the picker

        //Call the method to load the data and return
        //note that "fitler" is only applicable if I can add the parameter above
        return GetAllItemsFromTableAsync<TColItemType>(filter); 
    }


    /// <summary>
    /// Gets all data from the database table with the specified filter
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="filter"></param>
    /// <param name="cancelToken"></param>
    /// <returns></returns>
    protected async Task<List<TColItemType>> GetAllItemsFromTableAsync<TColItemType>(
            Expression<Func<TColItemType, bool>> filter = null) where TColItemType : Car, new()
    {
        //Do some prep work and null checks before calling the actual database method
        //

        //Call the database method to obtain the data
        return await database.GetAllWithChildrenAsync<TColItemType>(recursive: true, filter: filter);
    }


    #endregion Does NOT work

1 Ответ

0 голосов
/ 09 февраля 2019

Если вы напишите LoadFromDBAsync<Colors, List<Colors>>(x => x.ID == 4), это вызовет ваш метод LoadFromDBAsync.Если вы хотите передать его как делегат, вам нужно вместо этого передать лямбда-выражение:

await LoadPickerAsync<Car, ViewModel<Car>, List<Car>>(
  this.GetPropertyInfo(x => x.AvailableColors),
  this.GetPropertyInfo(y => y.ColorIndex),
  this.GetPropertyInfo(z => z.Color),
  () => LoadFromDBAsync<Colors, List<Colors>>(x => x.ID == 4));

Справочная информация: Сокращенная запись в вашем первом примере, передающая "группу методов" (то есть LoadFromDBAsync<Colors, List<Colors>> с параметром collectionLoaderAsync, работает только в том случае, если метод имеет одинаковую сигнатуру для типа параметра (т.е. Func<Task<TCollection>>). Во втором примере подпись не совпадает из-за дополнительного параметра filter.Поэтому требуется указанная выше полная лямбда-нотация.

...