5
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Net;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.UI.WebControls;
using dynamic = System.Linq.Dynamic;
using System.Linq.Expressions;

namespace Project.Lib.Extensions
{
    public static partial class Utils
    {
        public static List<T> SortForMe<T>(this List<T> list, string propertyName,SortDirection sortDirection)
        {
            string exp1 = string.Format("model.{0}", propertyName);
            var p1 = Expression.Parameter(typeof(T), "model");
            var e1 = dynamic.DynamicExpression.ParseLambda(new[] { p1 }, null, exp1);

            if (e1 != null)
            {
                if (sortDirection==SortDirection.Ascending)
                {
                    var result = list.OrderBy((Func<T, object>)e1.Compile()).ToList();
                    return result;
                }
                else
                {
                    var result = list.OrderByDescending((Func<T, object>)e1.Compile()).ToList();
                    return result;
                }
            }
            return list;
        }
    }
}

このコードを使用して、ジェネリック リストをプロパティ名で並べ替えています。プロパティ タイプが の場合string、このコードは正常に実行されますが、タイプがlongまたはの場合、次のint例外が発生します。

タイプ 'System.Func`2[Project.Lib.Model.UserQueryCount,System.Int64]' のオブジェクトをタイプ 'System.Func`2[Project.Lib.Model.UserQueryCount,System.Object]' にキャストできません。


var result = list.OrderBy((Func<T, dyamic>)e1.Compile()).ToList();

上記の行で、 を使用することにしdynamicましたが、再び例外が発生しました。私は何をすべきか?


メソッドを次のように変更しました。

public static List<TModel> SortForMe<TModel>(this List<TModel> list, string propertyName,SortDirection sortDirection) where TModel:class
    {
        var ins = Activator.CreateInstance<TModel>();
        var prop= ins.GetType().GetProperty(propertyName);
        var propertyType =  prop.PropertyType;

    string exp1 = string.Format("model.{0}", propertyName);
    var p1 = System.Linq.Expressions.Expression.Parameter(typeof(TModel), "model");
    var e1 = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p1 }, null, exp1);

    if (e1 != null)
    {
        if (sortDirection==SortDirection.Ascending)
        {
            return list.OrderBy((Func<TModel, propertyType>)e1.Compile()).ToList();
        }

        return list.OrderByDescending((Func<TModel, propertyType>)e1.Compile()).ToList();
    }
        return list;
}

リフレクションを使用して propertyType を取得しましたが、Func では次のように使用できませんでした:"Func<TModel, propertyType>"この問題を解決する方法はありますか

助けてくれてありがとう。

4

2 に答える 2

2

別の回答で述べたように、それが機能しない理由は確かにボクシングです。

class S
{
  public int f;
  public S s;
}

{
  Func<S, S> sGetter = s => s.s; // okay
  Func<S, object> objectGetter = s => s.s; // okay
  objectGetter = sGetter; // also okay
}

{
  Func<S, int> intGetter = s => s.f; // okay
  Func<S, object> objectGetter = s => s.f; // still okay
  objectGetter = intGetter; // not okay
}

機能するが最後の割り当てが機能しない理由Func<S, object> objectGetter = s => s.fは、実際には として解釈されFunc<S, object> objectGetter = s => (object)s.f、そのキャストが実際の操作を実行するためです。intGetterとはobjectGetter同じことをしないので、一方を他方として再解釈することはできません。これを認識して、そのキャストを自分で含めても問題ないはずです。私が知る限り、目的の戻り値の型を次のように指定するだけで、DQL (それはあなたが使用しているものですよね?) がそれを行いますobject

var e1 = DynamicExpression.ParseLambda(new[] { p1 }, typeof(object), exp1);
于 2012-08-04T13:35:15.990 に答える
0

これは値型のボクシングと関係があるのではないかと思いますが、よくわかりません。ただし、ボクシングを含まない回避策があり、問題を解決し、メソッドを少し使いやすくします。

並べ替えるプロパティを識別する文字列の代わりに、プロパティを返すラムダ式を実際に使用できます。これは、次の方法で実行できます。

private static string GetPropertyName<TObject, TProperty>(Expression<Func<TObject,     TProperty>> property)
{
    MemberExpression memberExpression = (MemberExpression)property.Body;
    PropertyInfo propertyInfo = (PropertyInfo)memberExpression.Member;

    return propertyInfo.Name;
}

Funcデリゲートをタイプにラップしていることに注意してください(詳細を読みExpressionたい場合があります)。そうすれば、式を調べて、どのメンバーが使用しているかを知ることができます。もちろん、これは単純なラムダを除くすべてで失敗するため、エラーチェックを行うことをお勧めします。x => x.Property

これを使用するには、拡張メソッドを少し変更する必要があります。

public static List<TObject> SortForMe<TObject, TProperty>(
    this List<TObject> list, 
    Expression<Func<TObject, TProperty>> property, 
    SortDirection sortDirection)
{
     string propertyName = GetPropertyName(property);
     ...
     ...          
     // when casting compiled expression to Func, instead of object, use TProperty
     // that way, you will avoid boxing
     var result = list.OrderBy((Func<T, TProperty>)e1.Compile()).ToList();
     ...
     ...
}

実際の使用法は次のようになります。

List<Foo> foos = ... ;
foos.SortForMe(x => x.IntegerProperty, SortDirection.Ascending);
foos.SortForMe(x => x.StringProperty, SortDirection.Ascending);

IntegerPropertyおよびは、タイプStringPropertyで定義された実際のプロパティです。Fooメソッドを使用する場合、実際の型パラメーターを指定する必要はありません。C#の型推論は、ここで非常にうまく機能します。

于 2012-08-03T20:30:33.640 に答える