Иногда нам необходимо получить доступ к закрытым членам, и если попытаться сделать это в 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);
Пример кода, который демонстрирует доступ к закрытому свойству:
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);