2017-02-18 3 views
1

표현식 트리를 통해 개체의 속성 값을 설정하는 데 필요한 유동적 인 API를 작성하려고합니다. 이 작업을 수행하는 대신 :C# 표현 트리 : 인터페이스에 엔터티 매개 변수 캐스팅

public static class Converters 
{ 
    public static SomeType ToSomeType(this Dictionary<string, string> values, string fieldName) 
    { 
     //...conversion logic 
    } 
} 

public class Target : ITarget 
{ 
    public SomeType Prop1 {get; set;} 

    public void SetValues(Dictionary<string, string> values) 
    { 
     Prop1 = values.ToSomeType("fieldName"); 
    } 
} 

내가이 작업을 수행 할 수 있도록하고 싶습니다 :

내가하는 SetProperty 정적 방법에 대한 몇 가지 진전을했습니다
public class Target : ITarget 
{ 
    public Target() 
    { 
     this.SetProperty(x=>x.Prop1, y => y.ToSomeType("fieldName")); 
    } 

    public void SetValues(Dictionary<string, string> values) 
    { 
     //...logic that executes compiled converter functions derived from 
     // SetProperty calls, and which are stored in an internal list 
    } 
} 

, 내가 문제로 실행 해요 넣어 내가 ITarget (내 예제에서 대상) 특정 클래스의 인스턴스 모두 같은 객체와 같은 참조 할 필요가 여기서

public static void SetProperty<TEntity, TProperty>(this TEntity target, Expression<Func<TEntity, object>> memberLambda, 
     Expression<Func<IImportFile, TProperty>> converter) 
     where TEntity: class, ITarget 
{ 
    var memberSelector = memberLambda.Body as MemberExpression; 
    if(memberSelector == null) 
     throw new ArgumentException(
       $"{nameof(SetProperty)} -- invalid property specification on Type {typeof(TEntity).FullName}"); 

    var propInfo = memberSelector.Member as PropertyInfo; 

    if(propInfo == null) 
     throw new ArgumentException(
       $"{nameof(SetProperty)} -- invalid property specification on Type {typeof(TEntity).FullName}"); 

    MethodCallExpression convMethod = converter.Body as MethodCallExpression; 
    if(convMethod == null) 
     throw new ArgumentException(
       $"{nameof(SetProperty)} -- converter does not contain a MethodCallExpression on Type {typeof(IImportFile).FullName}"); 

    ParameterExpression targetExp = Expression.Parameter(typeof(TEntity), "target"); 
    MemberExpression propExp = Expression.Property(targetExp, propInfo); 

    BinaryExpression assignExp = Expression.Assign(propExp, convMethod); 

    // this next line throws the exception 
    var junk = Expression.Lambda<Action<ITarget, IImportFile>>(assignExp, targetExp, 
       (ParameterExpression) convMethod.Arguments[ 0 ]).Compile(); 
} 

문제는하는 SetProperty 구현의 맨 마지막 줄에 발생합니다. 컴파일러는 두 번째 매개 변수 인 convMethod의 인수에서 파생 된 매개 변수를 허용하지 않습니다. 관련하여 TEntity! = ITarget입니다.

물론 내 예제에서 TEntity - Target은 ITarget을 구현하기 위해 정의 된 것을 제외하고는 :).

필자는 Expression 컴파일 코드가 실제로 엄격한 유형 검사를 수행하고 있다고 가정하고 매개 변수가 필요한 부분으로 캐스팅 될 수 있는지 여부를 확인하지 않습니다.

그러나 ParameterExpression을 다른 Type으로 캐스팅하는 방법을 알아낼 수는 있지만 여전히 동일한 매개 변수를 참조해야합니다. Expression.Convert()를 시도했지만 Expression.Lambda 호출이 ParameterExpression으로 사용되지 않는 UnaryExpression을 반환하기 때문에 작동하지 않습니다. 1

후속 번호는 내가 ITarget로, IImportTarget에 대한 참조를 수정했습니다. 혼란에 대해 죄송합니다.

꽤 큰 항목이기 때문에 전체 시스템을 설명하지 못했습니다. 특정 질문 - 두 개의 ParameterExpressions가 동일한 객체를 참조하는 방법은 무엇입니까? 공용 인터페이스) - 많은 곳에서 자랄 수있는 무언가입니다. HRESULT = 유형 ParameterExpression 'ConsoleApp1.TestTarget가' 는 '타입의 대리자 파라미터에 사용될 수 = -2147024809
메시지 ImportFramework.IImportTarget

System.ArgumentException 발생 : 여기

정확한 예외 메시지 인 '
자료 = System.Core 스택 트레이스 : System.Linq.Expression에서 System.Linq.Expressions.Expression.ValidateLambdaArgs에서 (타입 delegateType 표현 & 본체 ReadOnlyCollection 1 parameters) at System.Linq.Expressions.Expression.Lambda[TDelegate](Expression body, String name, Boolean tailCall, IEnumerable 1 개 파라미터) 표현식 본문 부울 tailCall, IEnumerable 1 parameters) at ImportFramework.ImportAgentExtensions.SetProperty[TEntity,TProperty](TEntity target, Expression 1 memberLambda, Expression`1 변환기) C : \ Programming \ ConnellCampaigns \ src \ ImportFramework \ ImportAgent에있는 표현식.CS : 라인 55의 InnerException :

var junk = Expression.Lambda<Action<TEntity, IImportFile>>(assignExp, targetExp, (ParameterExpression) convMethod.Arguments[ 0 ]).Compile(); 

및 적어도

+0

정확한 오류가 무엇인지 말할 수 있습니다. 또한 코드가 원하는 것을 실제로 볼 수 없습니다. 문자열 값을 어떤 종류의 객체로 변환하려고합니까? 그 당시에 '객체'가 더 좋지 않겠는가? 'ITmargetTarget'은 어디에서 정의 되었습니까? 실제로'ITarget'이 링크되어있는 것처럼 보이지는 않습니다. 그러나 당신의 예제가 불완전하기 때문에있을 수 있습니다 ('where' 절의 일부가 아닙니다). – Icepickle

+0

Please 후속 조치 # 1 참조 –

답변

0

용액 또는 사항 >> < < 용액 : 다음의 예외를 던진 마지막 행을 변경했다 이 방법이있을 때 나를 두 엔티티 유형 (TEntity)를 지정할 수

public static void ImportValues<TEntity>(this TEntity target, IImportFile importer) 
    where TEntity : class, IImportTarget 
{ 
    foreach(Action<TEntity, IImportFile> setter in ((IImportTargetSetValues) target).Setters) 
    { 
     setter(target, importer); 
    } 
} 

: 다음 또한 확장 방법을 사용하여 값을 설정하는 접근 방식을 변경 컴파일 된 시점과 사용 된 시점을 보여줍니다. TEntity 인스턴스와 관련된 목록에 평범한 오래된 개체로 setter를 저장했기 때문에 ImportValues ​​() 메서드 내의 루프에서 캐스트가 필요합니다.

개체 목록에있는 내용을 알지 못하기 때문에 문제가 될 수 있습니다. OTOH, 목록은 내부 인터페이스를 통해서만 사용할 수 있으며 LinkProperty 확장 메서드를 사용하여 추가 할 내용을 제어하므로 실제로 문제가되지 않습니다.