如何将Expression<Func<T, DateTime>>转换为Expression<Func<T, object>>?

66

我一直在搜索,但找不到如何从类型转换

Expression<Func<T, DateTime>>

转换为类型:

Expression<Func<T, object>>

所以我必须再次求助于 Stack Overflow 的丰富知识库 ;)
4个回答

84

你不能简单地在它们之间进行强制类型转换,因为它们并不是同一种类型的东西。不过,你可以在表达式树中有效地添加一个转换:

using System;
using System.Linq.Expressions;

class Test
{
    // This is the method you want, I think
    static Expression<Func<TInput,object>> AddBox<TInput, TOutput>
        (Expression<Func<TInput, TOutput>> expression)
    {
        // Add the boxing operation, but get a weakly typed expression
        Expression converted = Expression.Convert
             (expression.Body, typeof(object));
        // Use Expression.Lambda to get back to strong typing
        return Expression.Lambda<Func<TInput,object>>
             (converted, expression.Parameters);
    }

    // Just a simple demo
    static void Main()
    {
        Expression<Func<string, DateTime>> x = text => DateTime.Now;
        var y = AddBox(x);        
        object dt = y.Compile()("hi");
        Console.WriteLine(dt);
    }        
}

@JonSkeet Expression.Convert并不总是一个好主意。请看我的回答。 - Rookian
1
这将生成与仅声明x => x.TodayValue的Expression<Func<T, object>>相同的输出。 它最终创建了一个包含"Convert()"方法的表达式体,并且会导致一些像Linq to Entities这样的东西崩溃。 有没有办法让它在这种情况下工作?谢谢。 - CesarD
@CesarD:很抱歉,这不是我尝试过的事情,所以我没有什么建议。 - Jon Skeet

27
< p >来自RobJon Skeet的答案有一个问题。

你会得到类似于x => Convert(x.PropertyName)的东西,但是通常例如对于ASP.NET MVC,您需要像这样的表达式x => x.PropertyName

因此,Expression.Convert在某些情况下会“污染”表达式。

解决方案:

public static class LambdaExpressionExtensions
{
    public static Expression<Func<TInput, object>> ToUntypedPropertyExpression<TInput, TOutput> (this Expression<Func<TInput, TOutput>> expression)
    {
        var memberName = ((MemberExpression)expression.Body).Member.Name;

        var param = Expression.Parameter(typeof(TInput));
        var field = Expression.Property(param, memberName);
        return Expression.Lambda<Func<TInput, object>>(field, param);
    }
}

使用方法:

Expression<Func<T, DateTime>> expression = ...;
Expression<Func<T, object>> expr = expression.ToUntypedPropertyExpression();

7
似乎这并不起作用。例如,我似乎无法将 Int32 转换为 object;这就是 Convert 调用的作用。没有它,我会得到一个 ArgumentException。尝试使用 DateTime 版本,情况相同。如果这对您有用,请猜测您是使用引用类型完成的。 - Dave Cousineau
@rookian,你能否建议一种使用你的解决方案来处理嵌套属性的方法吗?目前,它对于直接属性非常有效,但我需要访问嵌套或子属性。如果可能,请提供更新版本。谢谢。 - Hardik Fefar

12
基于 Jon 提供的代码(顺便感谢一下),您可以采取进一步的措施以实现完全的灵活性。
public static Expression<Func<TModel, TToProperty>> Cast<TModel, TFromProperty, TToProperty>(Expression<Func<TModel, TFromProperty>> expression)
{
    Expression converted = Expression.Convert(expression.Body, typeof(TToProperty));

    return Expression.Lambda<Func<TModel, TToProperty>>(converted, expression.Parameters);
}

1
这个有使用示例吗? - Teoman shipahi
它不起作用。看起来Expression.Convert没有转换任何内容。 - Kate

2

只需将输出的TResult定义为对象并编译表达式,即可适用于所有数据类型;

Expression<Func<string, object>> dateExp = text => DateTime.Now;
object dt = dateExp.Compile()("hi");
Console.WriteLine(dt);

这里有一个Fiddle示例


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接