【问题标题】:WP7: Type.GetMethods throws MethodAccessException. Is there a workaround for this bug?WP7:Type.GetMethods 抛出 MethodAccessException。这个错误有解决方法吗?
【发布时间】:2011-05-09 11:15:04
【问题描述】:

我尝试获取一个类型的所有方法的列表。 Type 提供了 GetMethods 方法来执行此操作。但不幸的是,它似乎执行不正确。只要反射类型上没有覆盖的泛型方法,它就可以正常工作。在这种特殊情况下,会引发 MethodAccessException。

有没有人有解决这个 WP7 错误的方法?如果返回除泛型之外的所有方法,我很好。

这是一个将引发异常的类的示例。注意:没有泛型返回值是为了证明返回值与问题无关。此外,可以将基本方法更改为抽象方法,问题仍然存在。

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>();
    }
}

【问题讨论】:

  • incorrectly implemented. 我非常怀疑这一点。虽然,我看不到这样的异常记录:msdn.microsoft.com/en-us/library/td205ybf(v=vs.95).aspx
  • 这可能与我在 WP7 中使用 Silverlight 单元测试运行程序时遇到的问题有关 - 每当我在程序集中使用泛型类型(甚至未使用)时它都会崩溃。
  • @Jon Skeet 这正是我的用例。在分析已覆盖泛型方法的类时,此异常发生在我的 xUnit Unittest 运行程序实现中。
  • 我目前无法测试它,但是这篇文章有帮助吗? dev.flauschig.ch/wordpress/?p=74 他使用 MethodInfo 的IsGenericMethod 属性。
  • 不,这没有帮助。我尝试获取一种类型的所有方法信息。但我不在乎我是否真的得到了所有或没有通用的。这意味着我没有方法信息来检查它们是否是通用的。

标签: c# .net reflection windows-phone-7


【解决方案1】:

我会全部获取它们(注意GetMethods 中的BindingFlags.DeclaredOnly),然后过滤掉那些泛型方法(MethodInfo.IsGenericMethod)。

对不起 VB,我知道现在全世界都想要 C#,但是...

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

我刚刚使用您的示例类在 WP7 上进行了测试,效果很好。

【讨论】:

  • 不幸的是,我还需要基类中的方法,并且正如我已经提到的,我不关心过滤掉泛型。所以循环一点也不有趣。我只想能够获得一种类型的所有方法(但我可以接受不返回泛型的解决方案)。但仍然 +1 为我指明了正确的方向。似乎可以通过收集所有基类的所有方法来解决此错误。
  • 因将我引向正确方向而获得赏金。但由于它与答案相去甚远,我接受了我的扩展方法,其行为符合预期。
【解决方案2】:

我终于让它工作了。以下类型扩展方法返回与 .NET 4.0 实现完全相同的结果,但没有 WP7 引发的 MethodAccess 异常:

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;
    }
}

对于以下使用 .NET 4.0 的类以及在 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】:

    您真的会误解您所看到的行为吗?我认为您遇到了一个有效的安全访问问题,与在 WP7 上实施反射无关(除了它的安全模型)。

    看看this post。您正在反映的类型、任何有问题的方法或在您的特定情况下分配给 T 的类型是否可以用SecurityCriticalAttribute 标记为security critical

    【讨论】:

    • 在我复制了 OP 的示例后,我怀疑这个答案,因为我看不到该示例有任何明显的安全问题
    • 这绝对不是安全问题,因为您可以使用 GetMethods 重载的自定义实现获得与 .NET 4.0 相同的结果。
    • 在我自己玩弄之后我同意。
    【解决方案4】:

    我必须承认我怀疑您报告的内容,但它确实很容易重现,而且在我看来确实像一个错误。我认为你应该在Microsoft connect 上报告它。

    至于解决方法...

    这将使您获得派生类的成员,然后是基类(包括泛型),而不会出错:

    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);
    

    这可以概括(快速而肮脏的实现)有点像这样(EqualityComprarer 应该过滤掉被派生类覆盖或隐藏的基类成员):

     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);
    

    【讨论】:

      【解决方案5】:

      有一个GetMethods(BindingFlags)。尝试使用 BindingFlags 参数过滤掉您获得的方法。

      【讨论】:

      • 据我所知,没有办法使用绑定标志过滤泛型方法。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-29
      • 1970-01-01
      • 1970-01-01
      • 2021-03-11
      • 2012-02-28
      • 2011-11-03
      • 1970-01-01
      相关资源
      最近更新 更多