WP7: Type.GetMethods throws a MethodAccessException. Is there a workaround for this error?

I am trying to get a list of all type methods. The type provides the GetMethods method for this. But, unfortunately, this is wrong. It works correctly if there is a non-overridden generic method on the reflected type. In this special case, a MethodAccessException is thrown.

Does anyone have a workaround for this WP7 error? I am fine if all methods are returned except the general ones.

Here is an example of a class that will throw an exception. Note: no general return value is intended to prove that the return value is not involved in the task. In addition, the basic method can be changed to abstract, and the problem still remains.

public abstract class BaseClassWithGenericMethod { public virtual System.Collections.IList CreateList<T>() { return new List<T>(); } } public class DerivedClassWithGenericMethod : BaseClassWithGenericMethod { public override System.Collections.IList CreateList<T>() { return new List<T>(); } } 
+6
source share
5 answers

Finally, I started to work. The following type extension methods return exactly the same result as the .NET 4.0 implementation, but with no MethodAccess exceptions created by WP7:

 public static class TypeExtensions { public static MethodInfo GetMethodWp7Workaround(this Type type, string name) { if (name == null) { throw new ArgumentNullException("name"); } return GetMethod(type, name, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, null, CallingConventions.Any, null, null); } public static MethodInfo GetMethodWp7Workaround(this Type type, string name, Type[] types) { if (name == null) { throw new ArgumentNullException("name"); } if (types == null) { throw new ArgumentNullException("types"); } if (types.Any(t => t == null)) { throw new ArgumentNullException("types"); } return GetMethod(type, name, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, null, CallingConventions.Any, types, null); } public static MethodInfo GetMethodWp7Workaround(this Type type, string name, BindingFlags bindingAttr) { if (name == null) { throw new ArgumentNullException("name"); } return GetMethod(type, name, bindingAttr, null, CallingConventions.Any, null, null); } public static MethodInfo GetMethodWp7Workaround(this Type type, string name, Type[] types, ParameterModifier[] modifiers) { if (name == null) { throw new ArgumentNullException("name"); } if (types == null) { throw new ArgumentNullException("types"); } if (types.Any(t => t == null)) { throw new ArgumentNullException("types"); } return GetMethod(type, name, BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance, null, CallingConventions.Any, types, modifiers); } public static MethodInfo GetMethodWp7Workaround(this Type type, string name, BindingFlags bindingAttr, Binder binder, Type[] types, ParameterModifier[] modifiers) { if (name == null) { throw new ArgumentNullException("name"); } if (types == null) { throw new ArgumentNullException("types"); } if (types.Any(t => t == null)) { throw new ArgumentNullException("types"); } return GetMethod(type, name, bindingAttr, binder, CallingConventions.Any, types, modifiers); } public static MethodInfo GetMethodWp7Workaround(this Type type, string name, BindingFlags bindingAttr, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) { if (name == null) { throw new ArgumentNullException("name"); } if (types == null) { throw new ArgumentNullException("types"); } if (types.Any(t => t == null)) { throw new ArgumentNullException("types"); } return GetMethod(type, name, bindingAttr, binder, callConvention, types, modifiers); } private static MethodInfo GetMethod( Type type, string name, BindingFlags bindingFlags, Binder binder, CallingConventions callConvention, Type[] types, ParameterModifier[] modifiers) { if ((bindingFlags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) { return types == null ? type.GetMethod(name, bindingFlags) : type.GetMethod(name, bindingFlags, binder, callConvention, types, modifiers); } bool isBaseType = false; bindingFlags = bindingFlags | BindingFlags.DeclaredOnly; MethodInfo result = null; while (result == null && type != null) { result = types == null ? type.GetMethod(name, bindingFlags) : type.GetMethod(name, bindingFlags, binder, callConvention, types, modifiers); if (isBaseType && result != null && result.IsPrivate) { result = null; } type = type.BaseType; if (!isBaseType) { isBaseType = true; bindingFlags = bindingFlags & (~BindingFlags.Static); } } return result; } public static MethodInfo[] GetMethodsWp7Workaround(this Type type) { return type.GetMethodsWp7Workaround(BindingFlags.Public | BindingFlags.Static | BindingFlags.Instance); } public static MethodInfo[] GetMethodsWp7Workaround(this Type type, BindingFlags flags) { if ((flags & BindingFlags.DeclaredOnly) == BindingFlags.DeclaredOnly) { return type.GetMethods(flags); } flags = flags | BindingFlags.DeclaredOnly; Type currentType = type; bool isBaseType = false; var methods = new List<MethodInfo>(); while (currentType != null) { var newMethods = currentType.GetMethods(flags).Where(m => ShouldBeReturned(m, methods, isBaseType)); methods.AddRange(newMethods); currentType = currentType.BaseType; if (!isBaseType) { isBaseType = true; flags = flags & (~BindingFlags.Static); } } return methods.ToArray(); } private static bool ShouldBeReturned( MethodInfo method, IEnumerable<MethodInfo> foundMethods, bool isCurrentTypeBaseType) { return !isCurrentTypeBaseType || (!method.IsPrivate && !HasAlreadyBeenFound(method, foundMethods)); } private static bool HasAlreadyBeenFound( MethodInfo method, IEnumerable<MethodInfo> processedMethods) { if (!method.IsGenericMethodDefinition) { return processedMethods.Any(m => m.GetBaseDefinition().Equals(method.GetBaseDefinition())); } return processedMethods.Any( m => m.Name == method.Name && HaveSameGenericArguments(m, method) && HaveSameParameters(m, method)); } private static bool HaveSameParameters(MethodInfo method1, MethodInfo method2) { var parameters1 = method1.GetParameters(); var parameters2 = method2.GetParameters(); return parameters1.Length == parameters2.Length && parameters1.All(parameters2.Contains); } private static bool HaveSameGenericArguments(MethodInfo method1, MethodInfo method2) { var genericArguments1 = method1.GetGenericArguments(); var genericArguments2 = method2.GetGenericArguments(); return genericArguments1.Length == genericArguments2.Length; } } 

You will get the same result for the following class with .NET 4.0 and a workaround for WP7:

 internal class TestBaseClass { private static void BasePrivateStaticMethod() { } private void BasePrivateMethod() { } private void BasePrivateGenericMethod<T>() { } protected static void BaseProtectedStaticMethod() { } protected void BaseProtectedMethod() { } protected void BaseProtectedGenericMethod<T>() { } protected virtual void OverriddenProtectedMethod() { } protected virtual void OverriddenProtectedGenericMethod<T>() { } internal static void BaseInternalStaticMethod() { } internal void BaseInternalMethod() { } internal void BaseInternalGenericMethod<T>() { } internal virtual void OverriddenInternalMethod() { } internal virtual void OverriddenInternalGenericMethod<T>() { } public static void BasePublicStaticMethod() { } public void BasePublicMethod() { } public void BasePublicGenericMethod<T>() { } public virtual void OverriddenPublicMethod() { } public virtual void OverriddenPublicGenericMethod<T>() { } } internal class TestClass : TestBaseClass { public string Property { get { return null; } } private static void PrivateStaticMethod() { } private void PrivateMethod() { } private void PrivateGenericMethod<T>() { } protected static void ProtectedStaticMethod() { } protected void ProtectedMethod() { } protected static void ProtectedGenericMethod<T>() { } internal static void InternalGenericMethod<T>() { } internal void InternalMethod() { } internal static void InternalStaticMethod() { } public static void PublicStaticMethod() { } public void PublicMethod() { } public static void PublicGenericMethod<T>() { } internal override void OverriddenInternalMethod() { base.OverriddenInternalMethod(); } protected override void OverriddenProtectedMethod() { base.OverriddenProtectedMethod(); } public override void OverriddenPublicMethod() { base.OverriddenPublicMethod(); } internal override void OverriddenInternalGenericMethod<T>() { base.OverriddenInternalGenericMethod<T>(); } protected override void OverriddenProtectedGenericMethod<T>() { base.OverriddenProtectedGenericMethod<T>(); } public override void OverriddenPublicGenericMethod<T>() { base.OverriddenPublicGenericMethod<T>(); } } 
+3
source

I would grab them all (note the BindingFlags.DeclaredOnly in GetMethods ) and then filter out those that are common methods ( MethodInfo.IsGenericMethod ).

Sorry for VB, I know the whole world wants C # these days, but ...

 Public Function GetListOfMethods() As List(Of MethodInfo) Dim d As New DerivedClassWithGenericMethod Dim myArrayMethodInfo() As Reflection.MethodInfo myArrayMethodInfo = d.GetType.GetMethods(BindingFlags.Instance _ Or BindingFlags.Public _ Or BindingFlags.DeclaredOnly) Dim myArrayMethodInfoList As New List(Of MethodInfo) For Each m As MethodInfo In myArrayMethodInfo If Not m.IsGenericMethod Then myArrayMethodInfoList.Add(m) End If Next Return myArrayMethodInfoList End Function 

I just tested WP7 using your sample classes and it works great.

+3
source

Could you misinterpret the behavior you see? I think that you are faced with the real issue of access to security and are not related to the implementation of WP7 reflection (except for its security model).

Check out this post . Can the type that you reflect, any of the methods in question, or the Type assigned to T in your particular case, be marked as a critical level of security with SecurityCriticalAttribute ?

+1
source

I have to admit that I doubt what you are reporting, but it is really easy to reproduce and looks like an error to me. I think you should report this to Microsoft connect .

How to work around ...

This will lead you to the members of the derived class, and then to the base class (including the general) without errors:

 var m = new DerivedClassWithGenericMethod(); foreach (var method in m.GetType().GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) Debug.WriteLine(method.Name); foreach (var method in m.GetType().BaseType.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly)) Debug.WriteLine(method.Name); 

This can be generalized (quick and dirty implementation) as it was (EqualityComprarer should filter out elements of the base class that are overridden or hidden by derived classes):

  class MethodComparer : IEqualityComparer<MethodInfo> { public bool Equals(MethodInfo x, MethodInfo y) { return GetHashCode(x) == GetHashCode(y); } public int GetHashCode(MethodInfo obj) { int hash = obj.Name.GetHashCode(); foreach (var param in obj.GetParameters()) hash ^= param.ParameterType.GetHashCode(); if (obj.IsGenericMethodDefinition) { hash ^= obj.GetGenericArguments().Length.GetHashCode(); } else if (obj.IsGenericMethod) { foreach (var t in obj.GetGenericArguments()) hash ^= t.GetHashCode(); } return hash; } } static class Ext { public static MethodInfo[] MyGetMethods(this Type t) { if (t == null) return new MethodInfo[] { }; var methods = t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly); var baseMethods = from m in t.BaseType.MyGetMethods() where !methods.Contains(m, new MethodComparer()) select m; return methods.Concat(baseMethods).ToArray(); } } var m = new DerivedClassWithGenericMethod(); foreach (var method in m.GetType().MyGetMethods()) Debug.WriteLine(method.Name); 
+1
source

Theres a GetMethods(BindingFlags) . Try filtering out the methods that you get with the BindingFlags argument.

0
source

Source: https://habr.com/ru/post/885745/


All Articles