簡體   English   中英

從反射到表達式樹

[英]Moving from reflection to expression-tree

我有一個反射代碼,用於創建List<>的實例(運行時已知的類型參數),然后調用Add方法向其中添加一些值。 我的片段是這樣的:

// here is my type parameter
var genericType = typeof(MyRunTimeType);
// here is a list of my values
MyRunTimeType[] values = MyRunTimeValuesOfTypeMyRunTimeType();

// creating instance of List<>
var listType = typeof(List<>);
var listGenericType = listType.MakeGenericType(genericType);
var listInstance = Activator.CreateInstance(listGenericType);

// getting Add method and call it
var addMethod = listGenericType.GetMethod("Add", genericType);
foreach (var value invalues)
    addMethod.Invoke(listInstance, new[] { value });

因此,您如何建議將此反射代碼段轉換為表達式樹?

更新:

好吧,我寫了下面的代碼片段,似乎無法使用:

public static Func<IEnumerable<object>, object> GetAndFillListMethod(Type genericType) {

    var listType = typeof(List<>);
    var listGenericType = listType.MakeGenericType(genericType);

    var values = Expression.Parameter(typeof(IEnumerable<>).MakeGenericType(genericType), "values");

    var ctor = listGenericType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null);
    var instance = Expression.Parameter(listGenericType, "list");

    var assign = Expression.Assign(instance, Expression.New(ctor));

    var addMethod = listGenericType.GetMethod("AddRange", new[] { typeof(IEnumerable<>).MakeGenericType(genericType) });

    var addCall = Expression.Call(instance, addMethod, new Expression[] { values });

    var block = Expression.Block(
          new[] { instance },
          assign,
          addCall,
          Expression.Convert(instance, typeof(object))
        );

    return (Func<IEnumerable<object>, object>)Expression.Lambda(block, values).Compile();
}

但是,我收到此錯誤:

Unable to cast object of type 
'System.Func`2[System.Collections.Generic.IEnumerable`1[System.String],System.Object]' 
to type 
'System.Func`2[System.Collections.Generic.IEnumerable`1[System.Object],System.Object]'.

有什么建議嗎?

工作方式:

public static Func<IEnumerable<object>, object> GetAndFillListMethod(Type genericType)
{
    var listType = typeof(List<>);
    var listGenericType = listType.MakeGenericType(genericType);

    var values = Expression.Parameter(typeof(IEnumerable<object>), "values");

    var ctor = listGenericType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null);

    // I prefer using Expression.Variable to Expression.Parameter
    // for internal variables
    var instance = Expression.Variable(listGenericType, "list");

    var assign = Expression.Assign(instance, Expression.New(ctor));

    var addMethod = listGenericType.GetMethod("AddRange", new[] { typeof(IEnumerable<>).MakeGenericType(genericType) });

    // Enumerable.Cast<T>
    var castMethod = typeof(Enumerable).GetMethod("Cast", new[] { typeof(IEnumerable) }).MakeGenericMethod(genericType);

    // For the parameters there is a params Expression[], so no explicit array necessary
    var castCall = Expression.Call(castMethod, values);
    var addCall = Expression.Call(instance, addMethod, castCall);

    var block = Expression.Block(
            new[] { instance },
            assign,
            addCall,
            Expression.Convert(instance, typeof(object))
        );

    return (Func<IEnumerable<object>, object>)Expression.Lambda(block, values).Compile();
}

您的問題在於您試圖返回一個Func<IEnumerable<object>, object>但實際上您的func是一個Func<IEnumerable<T>, object> 解決方案是使參數成為IEnumerable<object> ,然后在傳遞給AddRange之前使用Enumerable.Cast<T>

我已經將instance使用的Expression.Parameter更改為Expression.Variable ...,但這只是為了更清楚地表明它是變量,而不是參數。 Expression.VariableExpression.Parameter生成的表達式樹是相同的(因為兩個函數具有相同的代碼)。 使用它的上下文定義它是參數還是變量。 我做了另一個小的更改: Expression.Call不需要為參數進行顯式的數組初始化。

啊...請注意,該Block的最后一行可能是:

addCall,
instance

而不是

addCall,    
Expression.Convert(instance, typeof(object))

因為任何引用類型都可以隱式轉換為object

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM