C#:GetMethod(按类型)(泛型列表)
本文关键字:泛型 列表 类型 GetMethod | 更新日期: 2023-09-27 18:08:19
我有一个类,它包含一些通用重载方法。我正试图通过它的参数类型来获得一个特定的。当我坚持前两个(使用int和string类型的参数(时,这相对容易做到。但无论我做什么,我都无法让我的程序注意到第三个,用于泛型列表。我用错Type参数了吗?如果是,正确的方法是什么?
/* rest of code */
static void Main(string[] args) {
MethodInfo method =
typeof(c).GetMethod("m", new Type[] { typeof(int) });
Console.WriteLine(method);
method =
typeof(c).GetMethod("m", new Type[] { typeof(String) });
Console.WriteLine(method);
method =
typeof(c).GetMethod("m", new Type[] { typeof(IEnumerable<>) });
Console.WriteLine(method);
Console.ReadKey();
}
}
static class c
{
public static void m<T>(int i)
{
}
public static void m<T>(String s)
{
}
public static void m<T>(IEnumerable<T> Ls)
{
}
}
简短版本:typeof(IEnumerable<>)
与typeof(IEnumerable<T>)
不同(对于某些T
(。
更长的版本:没有方法void c.m(IEnumerable<> Ls)
,只有重载,其中泛型参数将是特定的——在运行时存在——由于一些代码引用了泛型方法的实例化,因此需要抖动来创建方法。
在测试代码中,向泛型方法的某个实例添加一个调用,然后为该实例执行GetMethod
。
考虑以下内容:
using System.Collections.Generic;
using System.Linq;
using static System.Console;
class Methods {
public static void M(int x) {
// no-op
}
public static void M<T>(IEnumerable<T> x) {
// no-op
}
}
class Program {
static void Main(string[] args) {
Methods.M(0);
Methods.M(new[] { "a", "b" });
ShowAllM();
}
public static void ShowAllM() {
var tm = typeof(Methods);
foreach (var mi in tm.GetMethods().Where(m => m.Name == "M"))
{
WriteLine(mi.Name);
foreach (var p in mi.GetParameters())
{
WriteLine($"'t{p.ParameterType.Name}");
}
}
}
}
产生输出:
MInt32MIEnumerable `1
请注意,泛型重载只有一个结果。如果对M<char>(…)
的调用被添加到Main
,则输出是相同的。
对于反射,只有一个方法,它的参数反映了它的"开放泛型"性质,但这与用开放泛型类型(例如IEnumerable<>
(调用不太一样,因为开放类型是不可实例化的。
(我在这里篡改了很多技术细节。看看typeof(IEnumerable<>)
和typeof(IEnumerable<int>)
之间的调试器差异很有指导意义。(
第三个方法的签名为m<T>(IEnumerable<T>)
,但您的示例显示了查找签名为m(IEnumerable<>)
的方法的尝试。
typeof(IEnumerable<T>)
和typeof(IEnumerable<>)
之间的区别在于,前者是泛型类型,后者是泛型类型定义,它们不是一回事。泛型类型由泛型类型定义和泛型类型参数确定。
考虑到这一点,你会想使用:
method =
typeof(c).GetMethod("m", new Type[] { typeof(IEnumerable<MyType>) });
并替换将要传递到方法中的enumerable的类型。
另一方面,如果你不知道可枚举的类型,你可以获得通用方法定义,并在需要时生成可用的通用方法:
methodDef =
typeof(c).GetMethod("m", new Type[] { typeof(IEnumerable<object>) }).GetGenericMethodDefinition();
method = methodDef.MakeGenericMethod(new Type[] { typeof(MyType) });
如果从int和string方法中删除通用防御:
public static void m(int i)
{
}
public static void m(String s)
{
}
public static void m<T>(IEnumerable<T> Ls)
{
}
并使用以下行获得所需的通用方法:
method = typeof(c).GetMethods().FirstOrDefault(m => m.IsGenericMethod &&
m.GetParameters()[0].ParameterType.GetGenericTypeDefinition()
== typeof(IEnumerable<>));
这将完成
/// <summary>
/// Will fetch first occurence of IEnumerable<T> method and generate new generic method
/// <para/>
/// that corresponds to Document type
/// </summary>
/// <param name="Document"></param>
/// <param name="MethodName"></param>
/// <returns></returns>
public static MethodInfo GetAppropriateCollectionGenericMethod(object SourceClass, dynamic Document, string MethodName)
{
//get all public methods
var publicMethods = SourceClass.GetType().GetMethods().Where(x => x.Name == MethodName && x.IsGenericMethod);
//filter out only useful methods
foreach (var goodMethod in publicMethods)
{
var methodParams = goodMethod.GetParameters();
var firstParameterType = methodParams[0].ParameterType;
//methods that has arguments like Ienumerable<T>, RepeatedField<T> and so on
var hasNested = firstParameterType.GenericTypeArguments.Length > 0;
if (hasNested == true)
{
//if we found first method with that name that has as parameter an IEnumerable<T> we are ok
var genericTypeDef = firstParameterType.GetGenericTypeDefinition();
if (genericTypeDef == typeof(IEnumerable<>))
{
//Recover current document type, even if it's a list of such types
Type documentType = GetDocumentNestedType(Document);
//simply create a generic method based on Document inner Type
return goodMethod.MakeGenericMethod(documentType);
}
}
}
return null;
}
你需要这个,以避免错误:
var hasNested=firstParameterType.GenericTypeArguments.Length>0
这将获取首次出现的:
public static void m<T>(IEnumerable<T> Ls)
{
}
并将生成一个你可以这样使用的方法:
var localMethod = GenericReflectionHelper.GetAppropriateCollectionGenericMethod(this, Document, nameof(Insert));
//we are relying on implicit casting
localMethod.Invoke(this, new object[] { Document });
全样本:
public void Insert<T>(T Document)
{
//Valid for Lists and Repeated Fields
if (Document is IEnumerable)
{
MethodInfo localMethod;
var tuple = Tuple.Create(Document.GetType(), nameof(Insert));
if (CachedMethodsRedirection.ContainsKey(tuple) == true)
{
localMethod = CachedMethodsRedirection[tuple];
}
else
{
localMethod = GenericReflectionHelper.GetAppropriateCollectionGenericMethod(this, Document, nameof(Insert));
CachedMethodsRedirection.Add(tuple, localMethod);
}
//we are relying on implicit casting
localMethod.Invoke(this, new object[] { Document });
}
else
{
DocumentSession.GetCollection<T>().Insert(Document);
}
}
public void Insert<T>(IEnumerable<T> Document)
{
DocumentSession.GetCollection<T>().Insert(Document);
}