首页 > 解决方案 > 如何调用需要 Func 的方法带有 lambda 表达式参数

问题描述

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

我想要传递一个泛型方法作为委托,我想传递该方法(或其派生方法)一个 lambda 表达式参数(实际上是一个数据库过滤器),最终将调用 GetAllItemsFromTableAsync 方法,提供必要的过滤器表达式.

是否可以将方法作为委托传递,并在采用参数时为同一方法提供 lambda 表达式?在我的代码中,您将看到两个示例区域,一个当前可以工作(虽然它与原始代码相比非常简化,但我没有编译它但认为它传达了基本思想)另一个标记为“不工作”是我想要的如果可能的话,喜欢以某种方式实现。目前不是因为我不知道如何引用传递的参数以便将其菊花链到另一个方法。这可以做到吗?

我对 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

标签: c#lambdadelegatesexpressionfunc

解决方案


如果您编写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));

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


推荐阅读