我尝试获取一个类型的所有方法的列表。 Type提供GetMethods方法来执行此操作。但不幸的是,它似乎被错误地实施了。只要在反射类型上有没有重写的泛型方法,它就能正常工作。在这种特殊情况下,抛出了MethodAccessException。
有没有人对此WP7错误有解决方法?如果返回除通用方法之外的所有方法,我很好。
以下是将抛出异常的类的示例。注意:none泛型返回值旨在证明返回值不涉及问题。此外,基本方法可以改为摘要,问题仍然存在。
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>();
}
}
答案 0 :(得分:3)
我会全部抓住它们(请注意BindingFlags.DeclaredOnly
中的GetMethods
),然后过滤掉那些通用方法(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 :(得分:3)
最后我开始工作了。以下Type扩展方法返回与.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>();
}
}
答案 2 :(得分:1)
你真的会误解你所看到的行为吗?我认为您遇到了有效的安全访问问题,与WP7上的反射实现无关(除了安全模型)。
看看this post。如果您要反映的类型,在您的特定情况下,任何有问题的方法或分配给T的类型都会被security critical标记为SecurityCriticalAttribute吗?
答案 3 :(得分:1)
我必须承认我怀疑你在报道什么,但它确实很容易重现,对我来说看起来像个错误。我想你应该在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);
答案 4 :(得分:0)
Theres a GetMethods(BindingFlags)
。尝试使用BindingFlags参数过滤掉你得到的方法。