2016-08-29 5 views
2

Возможно ли создать динамический метод на C# (или, возможно, других языках .NET) в качестве метода экземпляра уже существующего типа с доступом к этой «ссылке», частным и защищенным членам?Компилировать динамический экземпляр методом дерева выражений, с помощью этого, частного и защищенного доступа?

Законный доступ к частным/защищенным членам, без ограничений видимости видимости, для меня очень важен, как это возможно при использовании DynamicMethod.

Expression.Lambda CompileToMethod (MethodBuilder) вызов выглядит очень сложным для меня, и я еще не мог найти способ, чтобы создать правильную MethodBuilder для уже существующего типа/модуль

EDIT: Теперь я создал copy Действие < 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 частные/защищенные члены с полной проверки видимости.

Есть ли эквивалент при компиляции дерева выражений?

+0

Вы не можете изменить исходный код существующего типа, если это то, что вы хотите сделать. Вы можете создать только метод расширения, однако это не будет метод экземпляра этого типа, а статический метод в другом классе. Доступ к внутренним элементам типа может быть угрозой безопасности, не так ли? – HimBromBeere

+0

Ну, вы * * обходите пределы видимости таким образом. Вы получаете доступ к полям, к которым ранее мог обращаться только автор этого класса. Деревья выражений поддерживают частных членов, так почему бы не использовать их? 'Lambda.Compile()'. Вы можете использовать 'this', не являясь методом экземпляра. 'this' является просто скрытым параметром. – usr

+0

@usr: приведенный выше пример IL допускает только частный/защищенный доступ в IL, если я укажу типof (DestClass) в конструкторе DynamicMethod, а не если я использую перегрузку с модулем. Существует также другая перегрузка с флагом «skipVisibility», который, согласно моему показанию, отключает проверку, в то время как мой используемый конструктор сохраняет проверку видимости, но рассматривает IL как часть DestClass. Я не знаю о внутренних компонентах, но это то, на что это похоже, и что мне теперь хотелось бы использовать деревья выражений (я также проверю, могут ли они всегда получать доступ к частным/защищенным). –

ответ

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 список).

 Смежные вопросы

  • Нет связанных вопросов^_^