我尝试使用反射来检索接口及其基接口的所有方法的列表。
到目前为止,我有这个:
var methods = type.GetMethods().Concat(
type.GetInterfaces()
.SelectMany(@interface => @interface.GetMethods()));
我希望能够过滤掉在基础接口中声明的 shadow 方法的方法,即" new"方法:
public interface IBaseInterface
{
string Method();
}
public interface IInterfaceWithNewMethod : IBaseInterface
{
new string Method();
}
使用我当前的代码,结果包含两种方法 - 我只想检索IInterfaceWithMethod.Method
并过滤掉IBaseInterface.Method
。
小提琴:https://dotnetfiddle.net/fwVeLS
PS:如果有帮助,您可以假设我可以访问派生界面的具体实例。该实例的类型只能在运行时知道(它是一个动态代理)。
答案 0 :(得分:2)
嗯,一种肮脏的方式可能是手动检查方法签名是否匹配。
检查签名的方法可能如下所示:
public static bool HasSameSignature(MethodInfo potentiallyHidingMethod, MethodInfo baseMethod)
{
//different name, therefore not same signature
if (potentiallyHidingMethod.Name != baseMethod.Name)
return false;
//now we check if they have the same parameter types...
var potentiallyHidingMethodParameters = potentiallyHidingMethod.GetParameters();
var baseMethodParameters = baseMethod.GetParameters();
//different number of parameters, therefore not same signature
if (potentiallyHidingMethodParameters.Length != baseMethodParameters.Length)
return false;
for (int i = 0; i < potentiallyHidingMethodParameters.Length; i++)
{
//if a parameter type doesn't match, it's not the same signature
if (potentiallyHidingMethodParameters[i].ParameterType != baseMethodParameters[i].ParameterType)
return false;
}
//if we've gotten this far, they have the same name and parameters,
//therefore, it's the same signature.
return true;
}
然后检查派生接口方法以查看它们是否隐藏(或匹配任何基本接口方法的签名):
Type type = typeof(IInterfaceWithNewMethod);
var potentiallyHidingMethods = type.GetMethods();
var baseTypeMethods =type.GetInterfaces()
.SelectMany(@interface => @interface.GetMethods());
var hidingMethods = potentiallyHidingMethods
.Where(hiding => baseTypeMethods.Any(baseMethod => HasSameSignature(hiding, baseMethod)));
注意,这是一个天真的实现。如果有一个更简单的方法或角落案例,我不会感到惊讶。
编辑:稍微误解了所需的输出。使用上面的代码,这将为您提供所有基本接口方法以及派生接口方法,但过滤掉派生接口隐藏的任何基本接口方法:
var allMethodsButFavouringHiding = potentiallyHidingMethods.Concat(
baseTypeMethods.Where(baseMethod => !potentiallyHidingMethods.Any(potentiallyhiding => HasSameSignature(potentiallyhiding, baseMethod))));
EDITx2:我根据以下界面进行了测试:
public interface IBaseInterface
{
string BaseMethodTokeep();
string MethodToHide();
string MethodSameName();
}
public interface IInterfaceWithNewMethod : IBaseInterface
{
new string MethodToHide();
new string MethodSameName(object butDifferentParameters);
string DerivedMethodToKeep();
}
这会产生MethodInfo
:
MethodToHide (IInterfaceWithNewMethod)
MethodSameName (IInterfaceWithNewMethod)
DerivedMethodToKeep (IInterfaceWithNewMethod)
BaseMethodTokeep (IBaseInterface)
MethodSameName (IBaseInterface)
因此它保留了任何不被隐藏的基本接口方法,任何派生的接口方法(隐藏或其他方式),并且尊重任何签名更改(即不会隐藏的不同参数)。 / p>
EDITx3:添加了另一个带有重载的测试:
public interface IBaseInterface
{
string MethodOverloadTest();
string MethodOverloadTest(object withParam);
}
public interface IInterfaceWithNewMethod : IBaseInterface
{
new string MethodOverloadTest();
}
结果:
MethodOverloadTest() for IInterfaceWithNewMethod
MethodOverloadTest(object) for IBaseInterface
答案 1 :(得分:1)
我最终使用了Chris Sinclair和Thomas Levesque的答案。
它有点广泛,但它更强大。
更重要的是,我认为阅读和推理更容易,这是处理反思时的首要任务。我们都知道反射代码变得复杂和混乱是多么容易......
internal static class TypeExtensions
{
/// <summary>
/// Gets a collection of all methods declared by the interface <paramref name="type"/> or any of its base interfaces.
/// </summary>
/// <param name="type">An interface type.</param>
/// <returns>A collection of all methods declared by the interface <paramref name="type"/> or any of its base interfaces.</returns>
public static IEnumerable<MethodInfo> GetInterfaceMethods(this Type type)
{
var allMethods = type.GetMethods().Concat(
type.GetInterfaces()
.SelectMany(@interface => @interface.GetMethods()));
return allMethods.GroupBy(method => new Signature(method))
.Select(SignatureWithTheMostDerivedDeclaringType);
}
private static MethodInfo SignatureWithTheMostDerivedDeclaringType(IGrouping<Signature, MethodInfo> group)
{
return group.Aggregate(
(a, b) => a.DeclaringType.IsAssignableFrom(b.DeclaringType) ? b : a);
}
private sealed class Signature
{
private readonly MethodInfo method;
public Signature(MethodInfo method)
{
this.method = method;
}
public override bool Equals(object obj)
{
var that = obj as Signature;
if (that == null)
return false;
//different names, therefore different signatures.
if (this.method.Name != that.method.Name)
return false;
var thisParams = this.method.GetParameters();
var thatParams = that.method.GetParameters();
//different number of parameters, therefore different signatures
if (thisParams.Length != thatParams.Length)
return false;
//different paramaters, therefore different signatures
for (int i = 0; i < thisParams.Length; i++)
if (!AreParamsEqual(thisParams[i], thatParams[i]))
return false;
return true;
}
/// <summary>
/// Two parameters are equal if they have the same type and
/// they're either both "out" parameters or "non-out" parameters.
/// </summary>
private bool AreParamsEqual(ParameterInfo x, ParameterInfo y)
{
return x.ParameterType == y.ParameterType &&
x.IsOut == y.IsOut;
}
public override int GetHashCode()
{
int hash = 37;
hash = hash*23 + method.Name.GetHashCode();
foreach (var p in method.GetParameters())
{
hash = hash*23 + p.ParameterType.GetHashCode();
hash = hash*23 + p.IsOut.GetHashCode();
}
return hash;
}
}
}