2016-03-24 17 views
3

以下のメソッドを使用して、非スタティックなMethodInfoを呼び出すことができるコンパイル済みの式(Func)に変換します。MethodInfoから作成した式ツリーの明示的な変換

これはうまくいきます:参照オブジェクトと値型の両方を期待するメソッドで呼び出すことができます。

doubleが必要なパラメータを持つメソッドを呼び出してintを渡すメソッドとは異なり、このコンパイル済みの式はそれをサポートせず、InvalidCastExceptionをスローします。

これを変更して、通常のメソッド呼び出しで発生する同じタイプの暗黙的キャストをサポートするにはどうすればよいですか?

ボーナスに関する質問:instanceExpで​​またはReflectedTypeMethodInfoから使用する必要がありますか?

public Func<object, object[], object> Create(MethodInfo methodInfo) 
{ 
    var methodParams = methodInfo.GetParameters(); 
    var arrayParameter = Expression.Parameter(typeof(object[]), "array"); 

    var arguments = 
     methodParams.Select((p, i) => Expression.Convert(
      Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), p.ParameterType)) 
      .Cast<Expression>() 
      .ToList(); 

    var instanceParameter = Expression.Parameter(typeof(object), "controller"); 

    var instanceExp = Expression.Convert(instanceParameter, methodInfo.DeclaringType); 
    var callExpression = Expression.Call(instanceExp, methodInfo, arguments); 

    var bodyExpression = Expression.Convert(callExpression, typeof(object)); 

    return Expression.Lambda<Func<object, object[], object>>(
     bodyExpression, instanceParameter, arrayParameter) 
     .Compile(); 
} 

--- EDIT

ワーキング溶液である:

var changeTypeMethod = typeof(Convert).GetMethod("ChangeType", new Type[] { typeof(object), typeof(TypeCode) }); 
var arguments = 
    methodParams.Select((p, i) => 
     !typeof(IConvertible).IsAssignableFrom(p.ParameterType) 
      // If NOT IConvertible, don't try to convert it 
      ? (Expression)Expression.Convert(
       Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), p.ParameterType) 
      : 
      // Otherwise add an explicit conversion to the correct type to handle int <--> double etc. 
      (Expression)Expression.Convert(
       Expression.Call(changeTypeMethod, 
        Expression.ArrayAccess(arrayParameter, Expression.Constant(i)), 
        Expression.Constant(Type.GetTypeCode(p.ParameterType))), 
       p.ParameterType) 
     ) 
     .ToList(); 

答えて

4

問題は、C#コードのこの部分と同じである。

object a = 123; 
double b = (double)a; // InvalidCastException 

理由はそのaobjectであるため、それをdoubleにするために、それをアンラッピングしてからintdoubleに変換してください。言語は、キャストが1つのことだけを行うことを可能にします。あなたはobject内にラップintがあるということを伝えることで、明示的にこのキャストを行うにはどのようにコンパイラに指示する必要があります。

double b = (double)((int)a); // Works 

あなたのLINQの式の中で同じことを行うことができれば、コンパイル済みの式のように動作しますよくただし、式を生成する時点での実際のパラメータの型はわからない可能性がありますので、別の戦略、つまりConvert.ChangeTypeメソッドへの呼び出しを行い、同時に展開してキャストできます。

+0

OKですが、後で 'Func'が評価されるまで、' int'が 'object'パラメータの中で囲まれていることを知らないとどうしますか?オブジェクトからそれ自身の型に変換し、それを呼び出しのために必要なパラメータ型に変換するためにパラメータをラップするのに必要な魔法の表現は何ですか? –

+0

@IanMercer私はそれを認識したので、過去の私のプロジェクトで覚えている戦略を使って答えを更新しました。 – dasblinkenlight

+0

私はそれを変更してみましたが、まだInvalidCastExceptionがあります。投稿できるコードをお持ちですか? –

関連する問題