order (), Where()等在泛型函数中使用

本文关键字:函数 泛型 Where order | 更新日期: 2023-09-27 18:10:16

我目前正在摆弄通用存储库。我的测试应用程序是在c#。我编写了一个函数,打印出特定存储库中的所有项目。存储库是一个输入参数。整个块位于这篇文章的底部,目前函数本身看起来像这样:

static void PrintCollection<T, TKey>(IRepository<T, TKey> repo)
{
    // Error since compiler does not recognizes p.LastName
    //foreach (Person p in repo.GetItems().OrderBy(p => p.LastName)) Console.WriteLine(Convert.ToString(p));
    foreach (T p in repo.GetItems())
        Console.WriteLine(Convert.ToString(p));
}

它工作得很好,但它缺少的东西,如按特定属性排序在一个类中,如LastNamePersonProductNameProduct。我想知道是否有可能向PrintCollection<T, TKey>(...)发送一个额外的参数,并测试特定字段是否存在,然后按它排序。例如:

static void PrintCollection<T, TKey, TOrder>(IRepository<T, TKey> repo, TOrder o = default(TOrder))
{
    if (o == default(TOrder))
        foreach (T p in repo.GetItems()) Console.WriteLine(Convert.ToString(p));
    else
        // Order by o
}

目前,我不知道应该使用什么来测试字段和属性是否存在于类中。然后,如何在LINQ和Lambdsa表达式中使用它们来排序,排序,过滤集合。

感谢


下面是我的代码片段。我把不重要的部分删掉了。

主程序

class Program
{
    static void Main(string[] args)
    {
        PeopleRepository repPeople = new PeopleRepository();
        ProductsRepository repProduct = new ProductsRepository();
        PrintCollection(repPeople);
        PrintCollection(repProduct);
    }
    static void PrintCollection<T, TKey>(IRepository<T, TKey> repo)
    {
        // Error since compiler does not recognizes p.LastName
        //foreach (Person p in repo.GetItems().OrderBy(p => p.LastName)) Console.WriteLine(Convert.ToString(p));
        foreach (T p in repo.GetItems()) Console.WriteLine(Convert.ToString(p));
    }
}

IRepository

interface IRepository<T, TKey>
{
    IEnumerable<T> GetItems();
    T GetItem(TKey key);
    // ...
}

Repository for People

class PeopleRepository : IRepository<Person, string>
{
    private static List<Person> _proxy;
    public PeopleRepository(List<Person> people = null)
    {
        if(people == null) _proxy = Person.GetAllPeople();
        else _proxy = people;
    }
    public IEnumerable<Person> GetItems()
    {
        return _proxy;
    }
    public Person GetItem(string key)
    {
        return _proxy.SingleOrDefault(p => p.ID == key);
    }
}

产品存储库

class ProductsRepository : IRepository<Product, int>
{
    private static List<Product> _proxy;
    public ProductsRepository(List<Product> products = null)
    {
        if (products == null) _proxy = Product.GetAllProducts();
        else _proxy = products;
    }
    public IEnumerable<Product> GetItems()
    {
        return _proxy;
    }
    public Product GetItem(int key)
    {
        return _proxy.SingleOrDefault(p => p.ID == key);
    }
}

order (), Where()等在泛型函数中使用

可以,使用委托来选择order by子句:

static void PrintCollection<T, TKey, TOrder>(IRepository<T, TKey> repo, 
       Func<T,TOrder> orderBy)
{
    var items = repo.GetItems();
    if (orderBy != null)
        items = items.OrderBy(orderBy);
    foreach (T p in items)
        Console.WriteLine(Convert.ToString(p));
}

你可以这样称呼它:

PrintCollection(repo, x=>x.Name);

对于Where,需要Func<T,bool>类型的参数,过程与上述相同。

注意:如果您使用像EF这样的ORM, ORM需要整个表达式树才能为查询构建SQL语句。当我们传递一个委托时,排序或筛选不会转换为SQL。ORM加载数据,然后对其应用排序或过滤。

如果您想确保它们转换为SQL,返回类型必须是实现IQueryable的类型,并且函数的参数应该更改为orderByExpression<Func<T,TOrder>>whileExpression<Func<T,bool>>

将此方法定义为IEnumerable的扩展:

public static class IEnumerableExtenstions
{
    static void PrintCollection<T>(this IEnumerable<T> collection)
    {
        foreach (T p in collection)
            Console.WriteLine(Convert.ToString(p));
    }
}

然后你可以把它命名为:

repo.GetItems().Where(x=>x.Age>15).OrderBy(x=>x.Name).PrintCollection();

比为函数中的每个功能定义参数更灵活、更自然。