2016-03-24 17 views
3

tarafından oluşturulan İfade Ağaçlarındaki parametrelerin açık bir şekilde dönüştürülmesi Aşağıda, (durağan olmayan) bir MethodInfo'u derlediğim bir derlenmiş İfadeye (Func) dönüştüren aşağıdaki yönteme sahibim.MethodInfo

Bu harika çalışır: Hem referans nesneleri hem de değer türlerini bekleyen bir yöntemle çağırabilirim.

AMA

Ben double bekliyor bir parametre olan bir yöntemini çağırın ve ona bir int o desteklemeyen bu derlenmiş ifadeyi geçmek ve bir InvalidCastException atar olabilir orijinal yöntemle aksine.

Normal yöntem çağrısı sırasında gerçekleşen aynı tür örtük yayınları desteklemek için bunu nasıl değiştirebilirim?

Bonus soru: instanceExp, DeclaringType veya ReflectedType'u MethodInfo'dan mı kullanıyor?

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(); 
} 

--- DÜZENLEME

çalışma çözümdür:

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

nedeni:

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(); 

cevap

4

sorun C# kod parçası aynıdır Bu a bir object, bu yüzden bir double döküm m yapmak için ust paketleyin ve int'u double'a dönüştürün. Dil, oyuncuya sadece bir şey yapabilmeyi sağlar - ister istemez ya da dönüşür, ancak her ikisini de değil. Sen object içine sarılmış bir int olduğunu nasıl anlatarak bunu açıkça cast yapmak için derleyici söylemek gerekir: Eğer LINQ ifadede aynı şeyi olsaydı

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

, sizin derlenmiş ifadesi olarak çalışacaktır iyi. Ancak, ifadenizi oluşturduğunuz anda parametrenin gerçek türünü bilmiyor olabilirsiniz, bu nedenle farklı bir strateji için gitmek isteyebilirsiniz - çağrıyı aynı anda açıp atmak üzere Convert.ChangeType yöntemine yönlendirebilirsiniz.

+0

Tamam, ama bunu, 'Func' değerlendirilinceye kadar 'object' parametresinin içinde bir' int' kutulu olduğunu bilmediğimde bunu nasıl yaparım? Sihirli ifade nedir? Bu parametreyi, kendi türüne (nesneden) dönüştürmek için parametrenin etrafına sarmak ve sonra bunu arama için gerekli parametre türüne dönüştürmek zorunda mıyım? –

+0

@IanMercer Bunu fark ettim, bu yüzden cevabımı geçmişte projelerimden birinde kullanmayı hatırladığım stratejiyle güncelledim. – dasblinkenlight

+0

Gösterildiği gibi değiştirmeyi denedim ancak hala bir InvalidCastException alıyorum. Yazabileceğin bir kodun var mı? –

İlgili konular