出于测试目的,我要检查引用类中的一系列方法签名是否已在不同的静态类上实现。对于他们中的大多数人来说,以下是有效的:
private static IEnumerable<Signature> GetMethodSigs(Type type)
{
// Get MethodInfos, filter and project into signatures
var methods = type.GetMethods(
BindingFlags.Public
| BindingFlags.DeclaredOnly
| BindingFlags.Static
| BindingFlags.Instance)
.Where(mi => !mi.Name.StartsWith("get_"))
.Where(mi => !mi.Name.StartsWith("set_"))
.Select(o => new Signature(o.Name, o.ReturnType, o.GetParameters().Select(pi => pi.ParameterType)));
return methods;
}
private static MethodInfo FindMethod(Type type, Signature sig)
{
MethodInfo member = type.GetMethod(
sig.Name,
BindingFlags.Public | BindingFlags.Static,
null,
sig.ParameterTypes.ToArray(),
null);
return member;
}
public struct Signature
{
public string Name;
public Type ReturnType;
public IEnumerable<Type> ParameterTypes;
public Signature(string name, Type returnType, IEnumerable<Type> parameterTypes = null)
{
Name = name;
ReturnType = returnType;
ParameterTypes = parameterTypes;
}
}
这是测试类的一部分,但想象一下驱动该过程的以下代码:
foreach(var sig in GetMethodSigs(typeof(ReferenceClass)))
Console.WriteLine(FindMethod(typeof(TestClass), sig)?.ToString());
在ReferenceClass
上正好接受以下方法签名,但FindMethod()
未找到TestClass
上的等效方法(确实存在!):
public static void SomeMethod<T>(SomeDelegate<T> del)
GetMethods()
为我提供了del
参数(SomeDelegate`1
)的类型,但这显然不适合在GetMethod()
中搜索,{{1 }}为此输入返回FindMethod()
。
任何想法如何操纵null
返回的值来使用GetMethods()
搜索泛型方法(明显的细微之处在于委托中使用泛型参数,而不是简单的参数型)?
(我知道有GetMethod()
的版本只是取名,但由于某些方法名称被重载,我还需要搜索参数类型。)
答案 0 :(得分:1)
我很长一段时间没有注意到你在比较两种不同的类定义:
foreach(var sig in GetMethodSigs(typeof(ReferenceClass)))
Console.WriteLine(FindMethod(typeof(TestClass), sig)?.ToString());
这是值得注意的,因为如果类是相同的,您的上述代码将起作用。问题是处理泛型的方式(简要查看Type
源,看来Equals
使用引用比较,但我认为==
被卸载到编译器。重点是,不同的泛型方法类型不是参考等价物。但是验证实际发生的事情是留给读者的练习。
快速演示(您可以在交互式shell中运行)
public class D { public static void Method<T>(Action<T> t) { } }
(new D()).GetType().GetMethod("Method").GetParameters()[0].ParameterType == (typeof(Action<>))
输出:
false
但是,如果检查类型,两者看起来都是一样的:
> (new D()).GetType().GetMethod("Method").GetParameters()[0].ParameterType.ToString()
"System.Action`1[T]"
> (typeof(Action<>)).ToString()
"System.Action`1[T]"
好的,这就是问题演示。您应该能够通过更改GetMethodSigs中的select语句来解决此问题,以便在每个参数类型上调用GetGenericTypeDefinition()
:
.Select(o => new Signature(o.Name, o.ReturnType, o.GetParameters().Select(pi => pi.ParameterType.GetGenericTypeDefinition())));
您可以看到以下内容现在显示相等:
> (new D()).GetType().GetMethod("Method").GetParameters()[0].ParameterType.GetGenericTypeDefinition() == (typeof(Action<>)).GetGenericTypeDefinition()
true
当然,这仅适用于通用类型。你必须添加一些逻辑,只在上面的select语句中调用.GetGenericTypeDefinition()
。
来自https://stackoverflow.com/a/1855248/1462295
的更多信息编辑:
使用https://stackoverflow.com/a/7182379/1462295
中的代码(请注意上面的空课T
)
public class D
{
public static void Method<TT>(Action<TT> t) { }
public static void Method<TT>(TT t) { }
}
public class RefD
{
public static void Method<TT>(Action<TT> t) { }
public static void Method<TT>(TT t) { }
}
foreach(var sig in GetMethodSigs(typeof(RefD)))
Console.WriteLine(GetMethodExt(typeof(D), sig.Name, sig.ParameterTypes.ToArray())?.ToString());
输出
Void Method[TT](System.Action`1[TT])
Void Method[TT](TT)
答案 1 :(得分:1)
而不是Type.GetMethod()
重载中的一个,我最终在目标类上使用GetMethods()
并使用扩展方法循环遍历所有成员以比较每个参数类型(注意C#7仅本地功能):
public static bool Similar(this Type reference, Type type)
{
if (reference.IsGenericParameter && type.IsGenericParameter)
{
return reference.GenericParameterPosition == type.GenericParameterPosition;
}
return ComparableType(reference) == ComparableType(type);
Type ComparableType(Type cType)
=> cType.IsGenericType ? cType.GetGenericTypeDefinition() : cType;
}
如果符合以下条件,则认为两种类型是“相似的”:
==
运算符SomeMethod<T,S>(S parameter)
中,唯一的参数类型将被视为等于SomeMethod<T1,T2>(T2 parm)
中的参数类型但不 SomeMethod<T,S>(T parameter)
)。SomeMethod<T,S>(Action<T> parameter)
上的参数类型将与SomeMethod<T,S>(Action<S> parameter)
“相似”。这并不理想,但事实证明这是一个非常难的问题!它适用于我的用例,它涵盖了我的所有案例,并且由于项目的性质(分析遗留代码),不会出现新的案例。
Similar()
用于Type
的以下扩展程序,旨在替换Type.GetMethod()
并实现我上面提到的循环:
public static MethodInfo GetMethodWithGenerics(
this Type type,
string name, Type[] parameters,
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
{
var methods = type.GetMethods(flags);
foreach (var method in methods)
{
var parmeterTypes = method.GetParameters().Select(p => p.ParameterType).ToArray();
if (method.Name == name && parmeterTypes.Count() == parameters.Length)
{
bool match = true;
for (int i = 0; i < parameters.Length; i++)
match &= parmeterTypes[i].Similar(parameters[i]);
if (match)
return method;
}
}
return null;
}
正如BurnsBA在他的回答下面说的那样,对于泛型的内置反射支持似乎存在一些基本问题,似乎没有一个简单的解决方案来解决我原来的问题。我在考虑了BurnBA的答案之后得到了这个答案,并在另一个问题上考虑了he linked to。这个答案对于希望产生更全面的比较版本的人来说特别有用。
任何发现这个有用的人都应该考虑提升其中一个或两个。
答案 2 :(得分:0)
你可以试试像
这样的东西string name = "MyFunction";
Type type = typeof(Program);
MethodInfo member = type.GetMethods(BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static | BindingFlags.Instance)
.Single(m => m.Name == name && m.GetParameters().Select(p => p.ParameterType.Name).SequenceEqual(new [] { typeof(MyClass<>).Name }));
public void MyFunction<T>(MyClass<T> test){}