Expression <func <..>&gt; .Compile()的实际类型是什么?

时间:2018-02-01 09:52:00

标签: c# lambda expression

我想确保表达式不会被多次编译,所以我试着在字典中记住它们:

    static Dictionary<int, object> dict = new Dictionary<int, object>();
    static T testmethod<T>(Expression<Func<T>> e) {
        object result;
        if (!dict.TryGetValue(1, out result)) {
            result = e.Compile();
            dict.Add(1, e);
        }

        return ((Func<T>)result)();
    }


    static void Main(string[] args) {

        var firstTest = testmethod(() => default(int));
        var secondTest = testmethod(() => default(int));
     }

虽然testmethod的第一次调用没有错误,但第二次调用因InvalidCastException(翻译)而崩溃:

System.Linq.Expressions.Expression`1[System.Func`1[System.In32]] cannot be converted to System.Func`1[System.In32] 

该消息表明该表达式根本没有编译,但为什么它第一次起作用呢?我错过了什么?

2 个答案:

答案 0 :(得分:4)

e.Compile会返回Func<T>,但您要将源Expression<Func<T>>添加到字典中,然后您尝试将其转换为Func<T>第二个电话。将已编译的Func<T>添加到dict代替:

dict.Add(1, result);

答案 1 :(得分:3)

result = e.Compile();
dict.Add(1, e);

您没有将委托(已编译的表达式)添加到字典中,而是将表达式本身添加到字典中。在第二行中,将e替换为result

return ((Func<T>)result)();

您希望result成为代表。仅当字典没有包含键1的值并且上述两行被执行时才会出现这种情况。如果字典确实包含一个值(在第二次迭代中),那么result将是一个表达式,由于dict.Add(1, e);

PS:您可能通过将字典值限制为Delegate而不是object来解决该错误。