3

さまざまなタイプのコレクションがたくさんあり、各コレクションをさまざまなプロパティで並べ替えたいと思います。たとえば、プロパティで IEnumerable<Employee>並べ替えられName、プロパティで並べ替えられます。コレクションを並べ替えた後、コレクションをページ付けするために使用します。AgeIEnumerable<Department>NumberOfEmployeesDepartmentNamePaginatedList

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

OrderBy拡張メソッドに渡されるソートデリゲートである4番目のパラメーターに注意してください。

この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

SortingFactory任意の型のラムダを返す、より一般的なバージョンの が必要です。基本的に、これは文字列を並べ替えに使用できる厳密に型指定された Expression に変換します。

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私はそれを持っていませんが、それがnullでないことを確認したいかもしれません. これはまた、着信文字列がエンティティやコレクションではなく、スカラー プロパティであることを前提としています。これは少しトリッキーです。

于 2012-04-09T14:40:13.913 に答える
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
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.

多くの答えは、表現操作に向けられています。この問題を解決するにはハンマーが重すぎる気がします...

ここで行ったように、2 番目の汎用パラメーターを削除/非表示にする必要があります。

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 に答える
1

コードの主な問題は、ジェネリック型間の変換を試みることに起因します。ここに示す解決策はそれを避けるべきです。

Comparer<T>クラスを利用できるはずです。Employeesを並べ替える例を次に示します。

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 に答える