3

我有许多类型的集合我想按不同的属性对每个集合进行排序。比如IEnumerable<Employee>会按NameandAge属性排序, IEnumerable<Department>会按NumberOfEmployeesandDepartmentName属性排序。我PaginatedList习惯在对集合进行排序后对其进行分页。

public class PaginatedList<T> : List<T>
{
  public PaginatedList(IEnumerable<T> source, Int32 pageIndex, Int32 pageSize , Func<T,Object> orderBy)
  {
    this.AddRange(source.OrderBy(orderBy).Skip((PageIndex - 1) * PageSize).Take(PageSize));
  }
}

请注意第 4 个参数,它将传递给 OrderBy 扩展方法的排序委托。

我正在使用通用方法来生成第 4 个元素

public Func<T, Object> SortingFactory<T>(String sortby) 
{
  switch (typeof(T).ToString())
  {
    case "Employee":
      switch(sortby)
      {
        case "Name":
           return new Func<Employee,String>(delegate(Employee e) { return e.Name; });
           break;                            
        case "Age":
           return new Func<Employee,Int32>(delegate(Employee e) { return e.Age; });
           break;
      }
      break;
    case "Department":
      switch(sortby)
      {
        case "NumberOfEmployees":
           return new Func<Department,Int32>(delegate(Department d) { return d.NumberOfEmployees; });
           break;                            
        case "DepartmentName":
           return new Func<Department,String>(delegate(Department d) { return d.DepartmentName; });
           break;
      }
      break;
  }
}

但它给了我一个编译错误Cannot implicitly convert type 'System.Func<Employee,String>' to 'System.Func<T,object>'

我也尝试将输出标记为,Func<Object,Object>但我得到了同样的错误。

我犯了什么错误以及如何使用这种方法。

4

4 回答 4

1

说我理解得很好

public class PaginatedList<T> : List<T>
{
  public PaginatedList(IEnumerable<T> source, Int32 pageIndex, Int32 pageSize )
  {
    this.AddRange(GetOrderFor<T>().Skip((PageIndex - 1) * PageSize).Take(PageSize));
  }
}

public static class Helpers
    {

        public static Func<T, object> GetSortExpression<T>(string sortExpressionStr)
        {
            var param = Expression.Parameter(typeof (T), "x");
            var sortExpression = Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Property(param, sortExpressionStr), typeof(object)), param);
            return sortExpression.Compile();
        }

        public static IOrderedEnumerable<T> GetOrderFor<T>(this IEnumerable<T> list)
        {
            switch (typeof (T).Name)
            {
                case "Employee":
                    return list.OrderBy(GetSortExpression<T>("Name")).ThenBy(GetSortExpression<T>("Age"));
                case "Category":
                    return list.OrderBy(GetSortExpression<T>("Name")).ThenBy(GetSortExpression <T> ("Id"));
            }
            return null;
        }
    }

如果我误解了,我认为 GetSortExpression 方法的简单用法可以帮助您避免错误

case "Employee":
      switch(sortby)
      {
        case "Name":
           return Helpers.GetSortExpression<T>("Name");                           
        case "Age":
           return Helpers.GetSortExpression<T>("Age");
      }
于 2012-04-09T13:18:51.570 回答
1

您的代码的主要问题源于尝试在泛型类型之间进行转换。这里给出的解决方案应该避免这种情况。

您应该能够使用该Comparer<T>课程。以下是对 s 进行排序的示例Employee

class EmployeeComparer : Comparer<Employee>
{
    string property;

    public EmployeeComparer(string Property)
    {
        this.property = Property;
    }

    public override int Compare(Employee x, Employee y)
    {
        switch (this.property)
        {
            case "Name":
                return Comparer<string>.Default.Compare(x.Name, y.Name);
            case "Age":
                return Comparer<int>.Default.Compare(x.Age, y.Age);
            default:
                return 0;
        }
    }
}

DepartmentComparer将非常相似。

编辑:这是property抽象为基类的字段,经过修改EmployeeComparer

abstract class PropertyComparer<T> : Comparer<T>
{
    protected string property;

    public PropertyComparer(string Property)
    {
        this.property = Property;
    }
}

class EmployeeComparer : PropertyComparer<Employee>
{
    public EmployeeComparer(string Property) : base(Property)
    {

    }

    public override int Compare(Employee x, Employee y)
    ...
}

然后您可以编写一个函数来获取所需类型的比较器:

Comparer<T> GetComparer(string Property)
{
    // Sadly, you cannot switch on a Type
    if (typeof(T) == typeof(Employee))
    {
        return new EmployeeComparer(Property) as Comparer<T>;
    }
    else if (typeof(T) == typeof(Department))
    {
        return new DepartmentComparer(Property) as Comparer<T>;
    }
    else
    {
        return Comparer<T>.Default;
    }
}

那很可能属于PaginatedList该类:

public class PaginatedList<T> : List<T>
{
    Comparer<T> GetComparer(string Property)
    ...

    public PaginatedList(IEnumerable<T> source, int pageIndex, int pageSize, string orderBy)
    {
        Comparer<T> comparer = GetComparer(orderBy);
        this.AddRange(source.OrderBy(x => x, comparer).Skip((PageIndex - 1) * PageSize).Take(PageSize));
    }
}

HTH。我没有测试它,但是如果您发现错误,请发表评论。

于 2012-04-09T13:26:29.760 回答
1

您需要一个更通用的版本SortingFactory,它会返回任何类型的 lambda。基本上,这会将字符串转换为可用于排序的强类型表达式:

public Expression<Func<T, To>> SortingFactory<T, To>( String sortby )
{
    // Entity type
    System.Type dataType = typeof( T );

    // Entity - main parameter (x =>
    ParameterExpression rootExp = Expression.Parameter(dataType, "x" );

    // property (x => x.Property
    PropertyInfo pi = dataType.GetProperty( sortby );

    // put together
    Expression expr = Expression.Property( rootExp, pi );
    return Expression.Lambda<Func<T, To>>( expr, rootExp );
}

我没有它,但你可能想检查它pi不为空。这还假设传入的字符串是标量属性,而不是实体或集合 - 这有点棘手。

于 2012-04-09T14:40:13.913 回答
1
Cannot implicitly convert type 'System.Func<Employee,String>' to 'System.Func<T,object>'

此错误告诉您Func<X, string>不继承自Func<X, object>(即使字符串继承自对象)。这是一个非常常见的泛型错误! List<Customer>不继承自List<object>. 如果是这样,你可以摆脱这个:

List<Customer> c = new List<Customer>();
List<object> x = (List<object>) c;
x.Add(x)
//List<object> is-a object, so the statement is valid,
//  but a List<Customer> is not a Customer, breaks the instance referenced by c
// instead of breaking c's instance, you get a runtime exception on line 2 - invalid cast.

许多答案都转向表达操纵的东西。我觉得锤子太重了,无法解决这个问题......

您需要做的是删除/隐藏第二个通用参数,就像我在这里所做的那样。

public interface IOrderer<T>
{
  IOrderedEnumerable<T> ApplyOrderBy(IEnumerable<T> source);
  IOrderedEnumerable<T> ApplyOrderByDescending(IEnumerable<T> source);
  IOrderedEnumerable<T> ApplyThenBy(IOrderedEnumerable<T> source);
  IOrderedEnumerable<T> ApplyThenByDescending(IOrderedEnumerable<T> source);
} 

public class Orderer<T, U> : IOrderer<T>
{
  private Func<T, U> _orderFunc;
  public Orderer(Func<T, U> orderFunc)
  { _orderFunc = orderFunc; }
  public IOrderedEnumerable<T> ApplyOrderBy(IEnumerable<T> source)
  { return source.OrderBy(_orderFunc); }
  public IOrderedEnumerable<T> ApplyOrderByDescending(IEnumerable<T> source)
  { return source.OrderByDescending(_orderFunc); }
  public IOrderedEnumerable<T> ApplyThenBy(IOrderedEnumerable<T> source)
  { return source.ThenBy(_orderFunc); }
  public IOrderedEnumerable<T> ApplyThenByDescending(IOrderedEnumerable<T> source)
  { return source.ThenByDescending(_orderFunc); }
}  

然后你的方法:

public class PaginatedList<T> : List<T>
{
  public PaginatedList(
    IEnumerable<T> source,
    Int32 pageIndex, Int32 pageSize,
    IOrderer<T> orderer)
  {
    IEnumerable<T> query = orderer.ApplyOrderBy(source)
      .Skip((PageIndex - 1) * PageSize)
      .Take(PageSize)  
    this.AddRange(query);
  }
} 

或者,当你想要多重排序(不可避免地会发生)时,这个:

public class PaginatedList<T> : List<T>
{
  public PaginatedList(
    IEnumerable<T> source,
    Int32 pageIndex, Int32 pageSize,
    List<IOrderer<T>> orderers)
  {
    IEnumerable<T> query = source;

    if (orderers.Any())
    {
      IOrderer<T> firstOrder = orderers.First();
      IOrderedEnumerable<T> orderedQuery = firstOrder.ApplyOrderBy(source);
      foreach(IOrderer<T> nextOrder in orderers.Skip(1))
      {
        orderedQuery = nextOrder.ApplyThenBy(orderedQuery);
      }
      query = orderedQuery;
    }

    this.AddRange(query.Skip((PageIndex - 1) * PageSize).Take(PageSize));
  }
} 

之后,您只需向 IOrderer 和 Orderer 添加一些属性,以便它知道它是否应该是 asc/desc,并且您应该将所有排序都包装起来。


(更有趣,你的分拣工厂)

public IOrderer<T> SortingFactory<T>(String sortby)
{
  switch (typeof(T).ToString())
  {
    case "Employee":
       switch(sortby)
       {
         case "Name":
            return new Orderer<Employee, string>(e => e.Name); //hmm, not sure this will work.
            break;  
于 2012-04-09T21:07:58.287 回答