我在我的一个C#项目中使用反射:它是针对Windows 8.1和Windows Phone 8.1的可移植类库。
在那个项目中,我有一个名为IMyInterface的接口,它有一个方法DoSomething,带有泛型参数TGenericObject。我还有一个名为MyClass的类。有一次,我需要通过反射在指定的接口中查找方法DoSomething。所以,我使用Type类中的GetRuntimeMethod方法和实际参数的类型,在我的例子中是MyClass。
请记住,我在这里提供的示例只是为了突出我所面临的问题。实际情况是IMyInterface接口和MyClass类在另一个项目中。
这是交易:我期待GetRuntimeMethod返回DoSomething方法的MethodInfo,但它没有:返回null。
从IMyInterface找到DoSomething方法或者我是否需要弄脏手段,是否有一些我很容易丢失的东西?
public interface IMyInterface
{
void DoSomething<TGenericObject>(TGenericObject myGenericObject);
}
public class MyClass
{ }
class Program
{
static void Main(string[] args)
{
MyClass myClassInst = new MyClass();
MethodInfo methodInfo = typeof (IMyInterface).GetRuntimeMethod("DoSomething", new [] { myClassInst.GetType() });
}
}
答案 0 :(得分:1)
我能够编写自己的扩展方法,实际上是按照GetRuntimeMethod方法做的。困扰我的是我仍然不明白为什么.NET提供的GetRuntimeMethod方法在我的示例中返回null。
这是暂时解决我的问题的不完整的类。这是一种非常天真的方法,但它是一个起点。该课程中缺少很多东西,但至少,这是一个允许我继续学习的答案。
public static class TypeExtensions
{
#region Public Methods
/// <summary>
/// Looks for the method in the type matching the name and arguments.
/// </summary>
/// <param name="type"></param>
/// <param name="methodName">
/// The name of the method to find.
/// </param>
/// <param name="args">
/// The types of the method's arguments to match.
/// </param>
/// <returns></returns>
/// <exception cref="ArgumentNullException">
/// Thrown if:
/// - The name of the method is not specified.
/// </exception>
public static MethodInfo GetRuntimeMethod(this Type type, string methodName, Type[] args)
{
if (ReferenceEquals(type, null))
throw new NullReferenceException("The type has not been specified.");
if (string.IsNullOrEmpty(methodName))
throw new ArgumentNullException("methodName", "The name of the method has not been specified.");
var methods = type.GetRuntimeMethods().Where(methodInfo => string.Equals(methodInfo.Name, methodName, StringComparison.OrdinalIgnoreCase)).ToList();
if (!methods.Any())
return null; // No methods have the specified name.
if (methods.Count == 1)
{
MethodInfo methodInfo = methods.Single();
return IsSignatureMatch(methodInfo, args) ? methodInfo : null;
}
// Oh noes, don't make me go there.
throw new NotImplementedException("Resolving overloaded methods is not implemented as of now.");
}
#endregion
#region Private Methods
/// <summary>
/// Finds out if the provided arguments matches the specified method's signature.
/// </summary>
/// <param name="methodInfo"></param>
/// <param name="args"></param>
/// <returns></returns>
private static bool IsSignatureMatch(MethodBase methodInfo, Type[] args)
{
Debug.Assert(!ReferenceEquals(methodInfo, null), "The methodInfo has not been specified.");
// Gets the parameters of the method to analyze.
ParameterInfo[] parameters = methodInfo.GetParameters();
int currentArgId = 0;
foreach (ParameterInfo parameterInfo in parameters)
{
if (!ReferenceEquals(args, null) && currentArgId < args.Length)
{
// Find out if the types matchs.
if (parameterInfo.ParameterType == args[currentArgId])
{
currentArgId++;
continue; // Yeah! Try the next one.
}
// Is this a generic parameter?
if (parameterInfo.ParameterType.IsGenericParameter)
{
// Gets the base type of the generic parameter.
Type baseType = parameterInfo.ParameterType.GetTypeInfo().BaseType;
// TODO: This is not good v and works with the most simple situation.
// Does the base type match?
if (args[currentArgId].GetTypeInfo().BaseType == baseType)
{
currentArgId++;
continue; // Yeah! Go on to the next parameter.
}
}
}
// Is this parameter optional or does it have a default value?
if (parameterInfo.IsOptional || parameterInfo.HasDefaultValue)
continue; // Uhum. So let's ignore this parameter for now.
// No need to go further. It does not match :(
return false;
}
// Ye!
return true;
}
#endregion
}
答案 1 :(得分:0)
在.net标准中(不考虑性能):
public static MethodInfo ResolveMethod(this Type objType, string methodName, Type[] parameterTypes)
{
BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
List<MethodBase> regularMethods = new List<MethodBase>();
List<MethodBase> genericMethods = new List<MethodBase>();
foreach (MethodInfo methodInfo in objType.GetRuntimeMethods())
{
if (methodInfo.Name == methodName)
{
if (methodInfo.GetParameters().Length == parameterTypes.Length)
{
if (methodInfo.IsGenericMethod)
genericMethods.Add(methodInfo);
else
regularMethods.Add(methodInfo);
}
}
}
MethodInfo found = null;
if (regularMethods.Count > 0)
{
MethodBase[] regulaMethodsArray = regularMethods.ToArray();
found = Type.DefaultBinder.SelectMethod(flags, regulaMethodsArray, parameterTypes, null) as MethodInfo;
}
if (found == null)
{
MethodBase[] genericMethodsArray = genericMethods.ToArray();
foreach (MethodInfo method in genericMethods)
{
var templateTypes = GetTemplate(parameterTypes, method, out int genericCount);
found = Type.DefaultBinder.SelectMethod(flags, genericMethodsArray, templateTypes, null) as MethodInfo;
if (found != null)
{
found = found.MakeGenericMethod(GetReplacements(parameterTypes, templateTypes, genericCount));
break;
}
}
}
return found;
}
public static Type[] GetReplacements(Type[] parameterTypes, Type[] template, int genericCount)
{
Type[] result = new Type[genericCount];
int p = 0;
for (int i = 0; i < parameterTypes.Length; i++)
{
if (template[i].IsGenericMethodParameter)
{
result[p] = parameterTypes[p];
p++;
}
}
return result;
}
public static Type[] GetTemplate(Type[] parameterTypes, MethodInfo methodInfo, out int genericCount)
{
genericCount = 0;
Type[] result = new Type[parameterTypes.Length];
ParameterInfo[] p = methodInfo.GetParameters();
for (int i = 0; i < parameterTypes.Length; i++)
{
if (p[i].ParameterType.IsGenericParameter)
{
result[i] = Type.MakeGenericMethodParameter(i);
genericCount++;
}
else
{
result[i] = parameterTypes[i];
}
}
return result;
}
}