如何使用 lambda 表达式参数调用需要 Func<T> 的方法

How to call a method that needs a Func<T> with an lambda Expression argument

我知道我可以调用一个直接采用 lambda 表达式的泛型方法。
例如在我下面的代码中我可以直接调用这个方法但是这不会实现我需要的: GetAllItemsFromTableAsync(x => x.ID =3)

我想要的是将通用方法作为委托传递,我想将该方法(或其衍生物)传递给最终将调用 GetAllItemsFromTableAsync 方法的 lambda 表达式参数(实际上是数据库过滤器),提供必要的过滤器表达式。

是否可以将方法作为委托传递,并在需要参数时为同一方法提供 lambda 表达式?在我的代码中,您将看到两个示例区域,一个目前有效(虽然它比原始代码简化了很多,但我没有编译它但认为它传达了基本思想)另一个标记为 "does not work" 是我想要的如果可能的话,喜欢以某种方式实现。目前不是,因为我无法弄清楚如何引用传递的参数以便将其菊花链连接到其他方法。这能做到吗?

我对 lambda 有点陌生,老实说,我仍在尝试全神贯注。任何帮助将不胜感激。

           #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

如果您编写 LoadFromDBAsync<Colors, List<Colors>>(x => x.ID == 4),这将 调用 您的 LoadFromDBAsync 方法。如果您想将其作为委托传递,则需要传递一个 lambda 表达式:

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));

背景: 第一个示例中的 shorthand 符号,将 "method group" (即 LoadFromDBAsync<Colors, List<Colors>> 传递给 collectionLoaderAsync参数,仅当方法在参数类型(即 Func<Task<TCollection>>)上具有相同签名时才有效。在您的第二个示例中,由于额外的 filter 参数,签名不匹配。因此,上面的完整需要 lambda 表示法。