Динамическое объявление Func‹in T, out Result›

Учти это:

var propertyinfo = typeof(Customer).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;

теперь я хочу объявить

Func<int,orderType>

Я знаю, что это невозможно напрямую, так как ordertype находится во время выполнения, но есть ли обходной путь?

это именно то, что я хочу сделать:

var propertyinfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;

var param = Expression.Parameter(typeof(T), "x");
var sortExpression = (Expression.Lambda<Func<T, orderType>>
   (Expression.Convert(Expression.Property(param, sortExpressionStr), typeof(orderType)), param));

все это потому, что я хочу преобразовать:

Expression<Func<T,object>> to Expression<Func<T,orderType>>

или, если это невозможно, я хочу создать его с первого места с правильным типом, дело в следующем:

Я нахожусь внутри метода, который имеет type(Customer) и имя свойства этого типа, которое я хочу заказать по нему, я хочу создать дерево выражений сортировки, чтобы передать его Orderby (здесь).


person Stacker    schedule 20.09.2010    source источник
comment
Вы не можете объявить во время компиляции, что sortExpression имеет тип Expression<Func<T,orderType>>, потому что orderType неизвестно во время компиляции, а только во время выполнения. Как вы собираетесь использовать лямбда-выражение после компиляции?   -  person dtb    schedule 20.09.2010
comment
хорошо, забудьте о выражении sortexpression, есть ли какой-либо возможный способ упорядочения с помощью строки имени свойства?   -  person Stacker    schedule 20.09.2010
comment
Я расширил свой ответ.   -  person dtb    schedule 20.09.2010
comment
Кажется, это тот же вопрос, на который отвечено здесь. подход остается типобезопасным.   -  person Leon van der Walt    schedule 29.09.2010


Ответы (6)


Вы можете сделать это, используя определение открытого универсального типа, а затем создав из него конкретный тип:

typeof(Func<,>).MakeGenericType(typeof(int), orderType);

Однако то, что вы пытаетесь сделать (вызов Lambda<TDelegate>), невозможно напрямую. Вы должны вызвать Lambda без параметра типа:

var propertyinfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyinfo.PropertyType;

var param = Expression.Parameter(typeof(T), "x");
var sortExpression = Expression.Lambda(
        Expression.Convert(Expression.Property(param, sortExpressionStr),
                           orderType), 
        param));

Это создаст для вас правильное Func<,> за кулисами. Если вы хотите скомпилировать выражение и использовать делегат, вы можете сделать это только динамически с помощью

sortExpression.Compile().DynamicInvoke(param);

Если вы хотите вызвать метод расширения OrderBy для Queryable, все становится немного сложнее:

var propertyInfo = typeof(T).GetProperty(sortExpressionStr);
Type orderType = propertyInfo.PropertyType;

// first find the OrderBy method with no types specified
MethodInfo method = typeof(Queryable).GetMethods()
  .Where(m => m.Name == "OrderBy" && m.GetParameters().Length == 2)
  .Single();
// then make the right version by supplying the right types
MethodInfo concreteMethod = method.MakeGenericMethod(typeof(T), orderType);

var param = Expression.Parameter(typeof(T), "x");

// the key selector for the OrderBy method
Expression orderBy =
    Expression.Lambda(
        Expression.Property(orderParam, propertyInfo),
        orderParam);

// how to use:
var sequence = new T[0].AsQueryable(); // sample IQueryable

// because no types are known in advance, we need to call Invoke 
// through relection here
IQueryable result = (IQueryable) concreteMethod.Invoke(
                                   null, // = static
                                   new object[] { sequence, orderBy });
person Ruben    schedule 20.09.2010
comment
нет, приятель, typeof(orderType) не скомпилируется, я уже пробовал - person Stacker; 20.09.2010
comment
typeof(orderType) не логичен, так как вы пытаетесь ввести typeof во время компиляции, а ordertype только во время выполнения, я думаю - person Stacker; 20.09.2010
comment
Просто любопытно: зачем нужно Expression.Convert свойство, имеющее тип orderType, для типа orderType? - person dtb; 20.09.2010
comment
@dtb, когда вы делаете динамическую сортировку и не знаете, какое свойство будет передано! и какой его тип, когда он передается вам в виде строки - person Stacker; 20.09.2010
comment
к сожалению, это тоже не сработает, Рубен, потому что тогда я не смогу использовать возврат DynamicInvoke с linqClass.OrderBy (здесь); - person Stacker; 20.09.2010
comment
причиной возврата DynamicInvoke является объект, который не может вывести свой тип. - person Stacker; 20.09.2010
comment
Вы не будете звонить DynamicInvoke, это произойдет внутри Enumerable.OrderBy. Но вам также придется вызывать Enumerable.OrderBy динамически, поскольку вы также не знаете, каковы его аргументы универсального типа, до времени выполнения. - person Ben Voigt; 20.09.2010
comment
@Stacker: Но зачем вам его конвертировать? Это уже нужного типа, нет? - person dtb; 20.09.2010
comment
@dtb, мне не нужно его преобразовывать, я думал об этом как о возможном решении, но мне нужно создать его с самого начала, используя правильный тип, но я не мог, как я сказал, я внутри метода, который имеет тип (клиент ) и имя свойства времени, которое я хочу заказать, я хочу создать дерево выражений сортировки, чтобы передать его в Orderby (здесь) - person Stacker; 20.09.2010
comment
+1, я очень люблю ответы, подобные этому. Код красивый, и подобные вещи часто пригодятся, когда вы играете с дженериками через отражение. - person Dan Abramov; 29.09.2010

Вы можете использовать метод Type.MakeGenericType:

Type result = typeof(Func<,>).MakeGenericType(typeof(int), orderType);

Это должно работать:

public static IQueryable<T> OrderByField<T>(
    IQueryable<T> q, string sortfield, bool ascending)
{
    var p = Expression.Parameter(typeof(T), "p");
    var x = Expression.Lambda(Expression.Property(p, sortfield), p);

    return q.Provider.CreateQuery<T>(
               Expression.Call(typeof(Queryable),
                               ascending ? "OrderBy" : "OrderByDescending",
                               new Type[] { q.ElementType, x.Body.Type },
                               q.Expression,
                               x));
}

Из здесь.

person dtb    schedule 20.09.2010
comment
я не уверен, что это соответствует моему случаю, и я не уверен, как это проверить, в любом случае я решил проблему, в любом случае спасибо dtb за ваше время и усилия. - person Stacker; 20.09.2010
comment
@Stacker: Итак, как вы решили проблему? Другие могут наткнуться на ваш вопрос и столкнуться с той же проблемой. - person dtb; 20.09.2010
comment
я уже опубликовал решение и опубликовал, что не так в моем коде. спасибо, приятель - person Stacker; 21.09.2010

linqClass.OrderBy(GetSortExpression(sortstr));


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

это сработало, моя проблема заключалась в том, что я использовал для передачи дополнительный параметр Typeof (Object), а orderby говорил мне, что он не может сортировать по типу объекта. Спасибо всем

спасибо, dtb, я проверю, работает ли ваш ответ, и я приму его, если он сработает, если нет, я приму этот.

person Stacker    schedule 20.09.2010
comment
Это не работает для меня. Список‹Сотрудников› list = новый Список‹Сотрудников›(); /*заполнить список*/, когда я вызываю так: list.OrderBy(GetSortExpression(MyColName)) - person Prerak K; 29.03.2012

Вы хотите использовать Dynamic Linq, часть примера кода Visual Studio.

Пример кода с использованием Dynamic Linq

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

person sisve    schedule 20.09.2010
comment
да, это сработает, но я не хотел включать динамическую linq dll - person Stacker; 21.09.2010
comment
Он распространяется в виде исходного файла, а не сборки. Вы можете скомпилировать его в свою сборку, чтобы избежать лишних ссылок. Посмотрите в C:\Program Files (x86)\Microsoft Visual Studio 10.0\Samples\1033\CSharpSamples.zip и сжатый файл LinqSamples\DynamicQuery\DynamicQuery\Dynamic.cs - person sisve; 21.09.2010

Посмотрите, достаточно ли динамично мое решение.

public class Product
{
    public long ID { get; set; }
    public string Name { get; set; }
    public DateTime Date { get; set; }
}


static void Main(string[] args)
{
    List<Product> products = (from i in Enumerable.Range(1, 10)
                          select new Product { ID = i, Name = "product " + i, Date = DateTime.Now.AddDays(-i) }).ToList();  //the test case

    const string SortBy = "Date";  // to test you can change to "ID"/"Name"

    Type sortType = typeof(Product).GetProperty(SortBy).PropertyType;     // DateTime
    ParameterExpression sortParamExp = Expression.Parameter(typeof(Product), "p");    // {p}
    Expression sortBodyExp = Expression.PropertyOrField(sortParamExp, SortBy);   // {p.DateTime}
    LambdaExpression sortExp = Expression.Lambda(sortBodyExp, sortParamExp);   //   {p=>p.DateTime}
    var OrderByMethod = typeof(Enumerable).GetMethods().Where(m => m.Name.Equals("OrderBy") && m.GetParameters().Count() == 2).FirstOrDefault().MakeGenericMethod(typeof(Product), sortType);
    var result = OrderByMethod.Invoke(products, new object[] { products, sortExp.Compile() });
}

Исходя из вышеизложенного, нетрудно изменить Product на T, чтобы сделать его универсальным.

person Cheng Chen    schedule 20.09.2010
comment
Да, я мог бы использовать это, если я использую linq для перечисления, но не linq для sql, так как это приведет к сортировке записей, поэтому, если вы возьмете только первые 10 и пропустите это, это создаст проблемы, так как вам нужно сделать сортировку в sql, используя orderby метод, пока вы восстанавливаете записи! - person Stacker; 21.09.2010

Вы можете получить Type, связанный с Func<int,orderType>, если вы хотите, например. передайте его в CreateDelegate.

Но что вы в конечном итоге хотите с этим сделать? Может есть более простой подход.

person Ben Voigt    schedule 20.09.2010