如何调用需要带有lambda Expression参数的Func <t>的方法

时间:2019-02-09 05:26:24

标签: c# lambda delegates expression func

我知道我可以调用直接采用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

1 个答案:

答案 0 :(得分:0)

如果您写Case.Aux[Column[T],T],这将调用您的LoadFromDBAsync方法。如果要作为委托传递它,则需要传递一个lambda表达式:

T

背景:第一个示例中的速记符号是将“方法组”(即Mapper传递给Out参数),只有在方法具有相同的参数类型(即LoadFromDBAsync<Colors, List<Colors>>(x => x.ID == 4))上的签名。在第二个示例中,由于额外的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)); 参数,签名不匹配。因此,需要上述完整的lambda表示法。