2016-08-29 7 views
2

"this"참조, 비공개 및 보호 된 멤버에 액세스 할 수있는 이미 존재하는 유형의 인스턴스 메서드로 C# 또는 기타 .NET 언어로 동적 메서드를 만들 수 있습니까?표현식 트리를 사용하여 동적 인스턴스 메소드를 컴파일하십시오.이 액세스 권한은 개인적이고 보호 된 액세스입니까?

DynamicMethod에서 볼 수 있듯이 가시성 제한을 우회하지 않고도 개인/보호 된 회원에게 합법적으로 액세스하는 것이 중요합니다.

Expression.Lambda CompileToMethod은 (MethodBuilder)가 호출이 나를 위해 매우 복잡한보고, 나는 아직/모듈

편집을 이미 존재하는 유형에 대한 적절한 MethodBuilder을 만들 수있는 방법을 찾을 수 없습니다

: 지금 만든 복사 동작 < DestClass, ISourceClass >, 정적/확장 메서드와 마찬가지로 식 트리에서. Expression.Property (...) 액세스는 Reflection (PropertyInfo)에 의해 정의되며 Reflection을 통해 정의 된 경우 개인/보호 된 멤버에 액세스 할 수 있습니다. DynamicMethod와 IL처럼 생성 된 메서드가 가시성 검사를 사용하는 멤버처럼 동작하며 (일반 C# 코드보다 약간 빠름) 식 트리가 유지 관리하는 것이 훨씬 더 나은 것처럼 보입니다. 이처럼

, DynamicMethod 및 Reflection.Emit를 작업 :

public static DynamicMethod GetDynamicCopyValuesMethod() 
{ 
    var dynamicMethod = new DynamicMethod(
     "DynLoad", 
     null, // return value type (here: void) 
     new[] { typeof(DestClass), typeof(ISourceClass) }, 
      // par1: instance (this), par2: method parameter 
     typeof(DestClass)); 
      // class type, not Module reference, to access private properties. 

     // generate IL here 
     // ... 
} 

// class where to add dynamic instance method 

public class DestClass 
{ 
    internal delegate void CopySourceDestValuesDelegate(ISourceClass source); 

    private static readonly DynamicMethod _dynLoadMethod = 
     DynamicMethodsBuilder.GetDynamicIlLoadMethod(); 

    private readonly CopySourceDestValuesDelegate _copySourceValuesDynamic; 

    public DestClass(ISourceClass valuesSource) // constructor 
    { 
     _valuesSource = valuesSource; 
     _copySourceValuesDynamic = 
      (LoadValuesDelegate)_dynLoadMethod.CreateDelegate(
       typeof(CopySourceDestValuesDelegate), this); 
       // important: this as first parameter! 
    } 

    public void CopyValuesFromSource() 
    { 
     copySourceValuesDynamic(_valuesSource); // call dynamic method 
    } 

    // to be copied from ISourceClass instance 
    public int IntValue { get; set; } 

    // more properties to get values from ISourceClass... 
} 

이 동적 방법은 전체 가시성 검사와 DestClass/개인 보호 된 멤버에 액세스 할 수 있습니다.

표현식 트리를 컴파일 할 때 이에 상응하는 항목이 있습니까? 당신은 쉽게 코드를 사용하여 유형의 protected 멤버에 액세스 할 수 있도록

+0

원하는 경우 기존 유형의 소스 코드를 수정할 수 없습니다. 확장 메서드를 만들 수는 있지만 그 형식의 인스턴스 메서드는 아니지만 다른 클래스의 정적 메서드는 아닙니다. 유형의 내부에 액세스하는 것은 보안 위험 일 수 있습니다. 그렇지 않습니까? – HimBromBeere

+0

글쎄, 당신은 * 이런 식으로 시야 한계를 우회하고 있습니다. 이전에 해당 클래스의 작성자 만 액세스 할 수있는 필드에 액세스 할 수있게되었습니다. 표현 트리는 비공개 멤버를 지원합니다. 왜 그렇게 사용하지 않습니까? 'lambda.Compile()'. 인스턴스 메소드가 아니더라도'this'를 사용할 수 있습니다. 'this'는 숨겨진 매개 변수 일뿐입니다. – usr

+0

@usr : 위의 IL 예제는 DynamicMethod 생성자에서 typeof (DestClass)를 지정하면 IL에서 private/protected 액세스 만 허용합니다. 모듈에서 오버로드를 사용하는 경우가 아닙니다. "skipVisibility"플래그가있는 또 다른 오버로드가 있습니다. 사용 된 생성자가 가시성 검사를 유지하면서 IL을 DestClass의 일부로 처리하면서 독서에 따르면 검사를 사용하지 않습니다. 내부 구조에 대해서는 잘 모르지만,이게 어떻게 생겼는지, 그리고 지금 Expression 트리를 사용하고 싶습니다. (또한 항상 private/protected에 액세스 할 수 있는지 확인합니다.) –

답변

1

나는이 여러 번 해봤 : 이것은 하나의 오브젝트에서 다른 오브젝트로 복사하는 멤버 작업을 컴파일

static Action<object, object> CompileCopyMembersAction(Type sourceType, Type destinationType) 
{ 
    // Action input args: void Copy(object sourceObj, object destinationObj) 
    var sourceObj = Expression.Parameter(typeof(object)); 
    var destinationObj = Expression.Parameter(typeof(object)); 

    var source = Expression.Variable(sourceType); 
    var destination = Expression.Variable(destinationType); 

    var bodyVariables = new List<ParameterExpression> 
    { 
     // Declare variables: 
     // TSource source; 
     // TDestination destination; 
     source, 
     destination 
    }; 

    var bodyStatements = new List<Expression> 
    { 
     // Convert input args to needed types: 
     // source = (TSource)sourceObj; 
     // destination = (TDestination)destinationObj; 
     Expression.Assign(source, Expression.ConvertChecked(sourceObj, sourceType)), 
     Expression.Assign(destination, Expression.ConvertChecked(destinationObj, destinationType)) 
    }; 

    // TODO 1: Use reflection to go through TSource and TDestination, 
    // find their members (fields and properties), and make matches. 
    Dictionary<MemberInfo, MemberInfo> membersToCopyMap = null; 

    foreach (var pair in membersToCopyMap) 
    { 
     var sourceMember = pair.Key; 
     var destinationMember = pair.Value; 

     // This gives access: source.MyFieldOrProperty 
     Expression valueToCopy = Expression.MakeMemberAccess(source, sourceMember); 

     // TODO 2: You can call a function that converts source member value type to destination's one if they don't match: 
     // valueToCopy = Expression.Call(myConversionFunctionMethodInfo, valueToCopy); 

     // TODO 3: Additionally you can call IClonable.Clone on the valueToCopy if it implements such interface. 
     // Code: source.MyFieldOrProperty == null ? source.MyFieldOrProperty : (TMemberValue)((ICloneable)source.MyFieldOrProperty).Clone() 
     //if (typeof(ICloneable).IsAssignableFrom(valueToCopy.Type)) 
     // valueToCopy = Expression.IfThenElse(
     //  test: Expression.Equal(valueToCopy, Expression.Constant(null, valueToCopy.Type)), 
     //  ifTrue: valueToCopy, 
     //  ifFalse: Expression.Convert(Expression.Call(Expression.Convert(valueToCopy, typeof(ICloneable)), typeof(ICloneable).GetMethod(nameof(ICloneable.Clone))), valueToCopy.Type)); 

     // destination.MyFieldOrProperty = source.MyFieldOrProperty; 
     bodyStatements.Add(Expression.Assign(Expression.MakeMemberAccess(destination, destinationMember), valueToCopy)); 
    } 

    // The last statement in a function is: return true; 
    // This is needed, because LambdaExpression cannot compile an Action<>, it can do Func<> only, 
    // so the result of a compiled function does not matter - it can be any constant. 
    bodyStatements.Add(Expression.Constant(true)); 

    var lambda = Expression.Lambda(Expression.Block(bodyVariables, bodyStatements), sourceObj, destinationObj); 
    var func = (Func<object, object, bool>)lambda.Compile(); 

    // Decorate Func with Action, because we don't need any result 
    return (src, dst) => func(src, dst); 
} 

(TODO를 참조 목록).