2013-05-16 1 views
1

나는 동적으로 다음을 수행하려고 시도 약간의 기능을 작성했습니다 :<T, TRet>

 Func<object, object> fa = i => Convert.ChangeType(i, typeof (string)); 
     Func<int, string> fb = o => (string) fa((int)o); 

FUNC은 다음과 같다 :

/// <summary> 
    ///  Converts <see cref="Func{object, object}" /> to <see cref="Func{T, TResult}" />. 
    /// </summary> 
    public static Delegate Convert(Func<object, object> func, Type argType, Type resultType) 
    { 
     Contract.Requires(func != null); 
     Contract.Requires(resultType != null); 

     var param = Expression.Parameter(argType); 

     var converted = Expression.Convert(
      Expression.Call(func.Method, Expression.Convert(param, typeof (object))), 
      resultType); 

     var delegateType = typeof (Func<,>).MakeGenericType(argType, resultType); 
     return Expression.Lambda(delegateType, converted, param).Compile(); 
    } 
을 반군 폐쇄가없는 경우

지금이 작품을 좋아 -이 테스트에 통과 :

[Test] 
    public void When_Converting_Without_Closure_Then_Suceeds() 
    { 
     // Arrange 
     Func<object, object> f = i => Convert.ChangeType(i, typeof(string));    
     var sut = FuncConversion.Convert(f, typeof(int), typeof(string)); 

     // Act 
     var res = (string) sut.DynamicInvoke(10); 

     // Assert 
     Assert.AreEqual(typeof(Func<int, string>), sut.GetType()); 
     Assert.AreEqual("10", res); 
    } 

하지만 때 교류 losure이 테스트가 실패 관여 :

[Test] 
    public void When_Converting_With_Closure_Then_Succeeds() 
    { 
     // Arrange 
     var typeTo = typeof (string); 
     Func<object, object> f = i => Convert.ChangeType(i, typeTo);    
     var sut = FuncConversion.Convert(f, typeof(int), typeof(string)); 

     // Act 
     var res = (string)sut.DynamicInvoke(10); 

     // Assert 
     Assert.AreEqual(typeof(Func<int, string>), sut.GetType()); 
     Assert.AreEqual("10", res); 
    } 

System.ArgumentException 정적 메소드는 null 인스턴스는 비 정적 메소드가 null 인스턴스를 필요로 요구한다. 매개 변수 이름 : System.Linq.Expressions.Expression.Call에서 System.Linq.Expressions.Expression.ValidateStaticOrInstanceMethod (표현의 예를, MethodInfo 방법) 의 방법 (MethodInfo 방법, 표현적인 것들은 arg0)

어떤 생각 어떤 문제가 있는지 ?

+0

좋아, 나는 거기 FUNC에 대한 숨겨진 폐쇄 PARAM이고 나는 그것의 존재를 모르고, 무슨 무슨 내가 아는 생각합니다. Expression.Invoke를 사용해 보겠습니다. – DanH

+0

이런 식으로 수행보다는 매우 간단한 방법으로 어떤 이유'개인 정적 Func을 변환 (Func을 FUNC) {복귀 t => (T2) FUNC (t); }'? 그런 다음'var sut = Convert (f);' – Iridium

+0

예, 일반 매개 변수로 T1/T2가 없습니다. 동적으로 그 메소드를 호출 할 수 있습니다. 비록 오버 헤드가있을 때마다 호출됩니다. – DanH

답변

2

좋아요. 문제는 클로저에서 일반적으로 정적 메서드 인 func은 인스턴스 메서드의 대상 인스턴스 인 첫 번째 매개 변수가 클로저 상태를 유지하는 데 사용된다는 것입니다. 그래서 나는 그 상태가 존재하는지 확인하고 그것이 존재한다면 그것을 호출 할 필요가있다.

잇 짜잔 :

/// <summary> 
    ///  Converts <see cref="Func{object, object}" /> to <see cref="Func{T, TResult}" />. 
    /// </summary> 
    public static Delegate Convert(Func<object, object> func, Type argType, Type resultType) 
    { 
     // If we need more versions of func then consider using params Type as we can abstract some of the 
     // conversion then. 

     Contract.Requires(func != null); 
     Contract.Requires(resultType != null); 

     var param = Expression.Parameter(argType); 
     var convertedParam = new Expression[] {Expression.Convert(param, typeof (object))}; 

     // This is gnarly... If a func contains a closure, then even though its static, its first 
     // param is used to carry the closure, so its as if it is not a static method, so we need 
     // to check for that param and call the func with it if it has one... 
     Expression call; 
     call = Expression.Convert(
      func.Target == null 
      ? Expression.Call(func.Method, convertedParam) 
      : Expression.Call(Expression.Constant(func.Target), func.Method, convertedParam), resultType); 

     var delegateType = typeof (Func<,>).MakeGenericType(argType, resultType); 
     return Expression.Lambda(delegateType, call, param).Compile(); 
    }