About .NET, ASP.NET, MVC, C#, WPF, WCF and everything related to .NET and more.

Доступ к закрытым членам в Silverlight

Категории: Программирование

Иногда нам необходимо получить доступ к закрытым членам, и если попытаться сделать это в SL-приложении с использование рефлексии, мы получим ошибку:
Attempt by method 'method name' to access method 'member name' failed.

Используем динамические методы и emit

Первое, что я попробовал использовать был класс DynamicMethod. В нем определен конструктор с параметром restrictedSkipVisibility_, вот описание из документации:
  • restrictedSkipVisibility - значение true, чтобы пропустить проверки видимости, выполняемые JIT-компилятором, для типов и членов, доступ к которым имеет язык MSIL динамического метода.
К сожалению, когда я попробовал создать класс, используя, этот конструктор я получил ошибку:
Attempt by security transparent method 'method name' to access security critical method 'System.Reflection.Emit.DynamicMethod..ctor(System.String, System.Type, System.Type[], System.Type, Boolean)' failed.
Это произошло, потому что конструктор помечен, как SecurityCritical и мы не можем использовать его. В SL5 этот конструктор сделали internal.

Используем lamba expression

Последнее, что я попробовал был класс LambdaExpression, и я не ошибся. Если вы декомпилируете класс LambdaExpression, вы увидите, что он использует класс LambdaCompiler, для генерации динамических методов. Один из конструкторов класса LambdaCompiler выглядит следующим образом:
private LambdaCompiler(AnalyzedTree tree, LambdaExpression lambda)
{
    Type[] parameterTypes = CollectionExtensions.AddFirst<Type>((IList<Type>) LambdaCompiler.GetParameterTypes(lambda), 
        typeof (Closure));
    DynamicMethod dynamicMethod = new DynamicMethod(lambda.Name ?? "lambda_method", 
        lambda.ReturnType, parameterTypes, true);
    this._tree = tree;
    this._lambda = lambda;
    this._method = (MethodInfo) dynamicMethod;
    this._ilg = new OffsetTrackingILGenerator(dynamicMethod.GetILGenerator());
    this._hasClosureArgument = true;
    this._scope = tree.Scopes[(object) lambda];
    this._boundConstants = tree.Constants[lambda];
    this.InitializeMethod();
}
Обратите внимание на эту строчку кода:
DynamicMethod dynamicMethod = new DynamicMethod(lambda.Name ?? "lambda_method", 
        lambda.ReturnType, parameterTypes, true);
Это, то о чем я писал выше, здесь используется конструктор, который пропускает проверки видимости. Кроме того, это означает, что любой метод, который был создан с помощью класса LambdaExpression имеет доступ к закрытым членам.
Пример кода, который демонстрирует доступ к закрытому свойству:
public class Alpha
{
    public Alpha()
    {
    }

    private string _field;

    protected string Property { get; set; }

    protected void Method()
    {
                
    }
}

...

private static TResult CreatePropertyGetter<TResult>(PropertyInfo propertyInfo, 
				                  Type targetType, Type resultType)
{
    if (!propertyInfo.CanRead)
        throw new ArgumentException(string.Format("The property {0} in type {1} doesn't have getter.",
                                                    propertyInfo.PropertyType, propertyInfo.DeclaringType));

    ParameterExpression targetExp = Expression.Parameter(targetType, "target");
    MemberExpression propertyExp;
    //Checking if it's a static.
    if (propertyInfo.GetGetMethod(true).IsStatic)
        propertyExp = Expression.Property(null, propertyInfo);
    else
    {
        //Checking if target type is not equals to DeclaringType, we need to convert type.
        if (targetType != propertyInfo.DeclaringType)
            propertyExp = Expression.Property(Expression.Convert(targetExp, propertyInfo.DeclaringType),
                                                propertyInfo);
        else
            propertyExp = Expression.Property(targetExp, propertyInfo);
    }
    return Expression.Lambda<TResult>(Expression.Convert(propertyExp, resultType), targetExp).Compile();
}

const BindingFlags flags =  BindingFlags.NonPublic | BindingFlags.Instance
                            | BindingFlags.Public;
var propertyInfo = typeof(Alpha).GetProperty("Property", flags);


var alpha = new Alpha();
var propertyGetter = CreatePropertyGetter<Func<Alpha, string>>(propertyInfo, typeof (Alpha), 
			                        typeof (string));
string getter = propertyGetter(alpha);

MugenInjection

MugenInjection имеет класс ExpressionActivator, который использует LambdaExpression для доступа к членам. Также, MugenInjection имеет множество расширений, которые обеспечивают доступ к членам, этот код демонстрирует некоторые из них:
using MugenInjection;
.....

const BindingFlags flags =  BindingFlags.NonPublic | BindingFlags.Instance
                            | BindingFlags.Public;
var constructorInfo = typeof (Alpha).GetConstructor(Type.EmptyTypes);
var propertyInfo = typeof (Alpha).GetProperty("Property", flags);
var fieldInfo = typeof (Alpha).GetField("_field", flags);
var methodInfo = typeof (Alpha).GetMethod("Method", flags);

//Contains provider for member access.
//            InjectorUtils.ReflectionAccessProvider

//Creating new instance.
var instance = (Alpha) constructorInfo.CreateInstance();
            
//Getting the property as object
var objValue = propertyInfo.GetValueFast(instance);
//Getting the property as string
var strValue = propertyInfo.GetValueFast<Alpha, string>(instance);
//Setting the property.
propertyInfo.SetValueFast(instance, "test");

//Getting the field as object
objValue = fieldInfo.GetValueFast(instance);
//Getting the field as string
strValue = fieldInfo.GetValueFast<Alpha, string>(instance);
//Setting the field.
fieldInfo.SetValueFast(instance, "test");

//Invoking the method.
methodInfo.InvokeFast(instance);

Заключение

Если, вам нужен доступ к закрытым членам, вы можете использовать класс LambdaExpression или использовать расширяющие методы из библиотеки MugenInjection.

Комментарии
Оставить комментарий
*bold*
_italics_
+underline+
* Bullet List
** Bullet List 2
# Number List
## Number List 2
{"Do not apply formatting"}
{code:language} code here {code:language}.
Supports: aspx c#, c#, c++, html, sql, xml
[url:http://www.example.com]