Say one has a generic definition MethodInfo
object, that is, a MethodInfo
object such that methodInfo.IsGenericMethodDefinition == true
:
假设有一个泛型定义的方法信息对象,即一个方法信息对象,这样的方法Info.IsGenericMethodDefinition==TRUE:
MethodInfo methodInfo = somethingCool;
Say also that they also have a list of generic arguments:
也可以说它们也有一个泛型参数列表:
Type[] genericArguments = somethingElseAlsoCool;
I can get the types of the parameters of the method with:
我可以使用以下命令获取该方法的参数类型:
Type[] types = (
from ParameterInfo parameter
in methodInfo.GetParameters()
select parameter.ParameterType
).ToArray();
Now, I can fill in the generic arguments to methodInfo
with methodInfo.MakeGenericMethod
:
现在,我可以使用方法Info.MakeGenericMethod来填充方法信息的泛型参数:
MethodInfo invokableMethodInfo = methodInfo.MakeGenericMethod(genericArguments);
However, what about the array of Type
s? I can just prematurely increment though the types as such:
但是,类型数组又如何呢?我可以通过以下类型过早地递增:
for (int i = 0; i < types.Length; i++)
types[i] = types[i].MakeGenericType(genericArguments);
But this wouldn't make sense. Consider the following method:
但这说不通。请考虑以下方法:
void MyGenericMethod<TOne, TTwo, TThree>(IDictionary<TOne, TThree> Dictionary);
If I did the previously mentioned algorithm, then the types would map incorrectly:
如果我执行前面提到的算法,则类型将不正确地映射:
TOne => TKey //Fine, but only by coincidence.
TTwo => TValue //Incorrect. TValue should be TThree, not TTwo.
TThree => ? //What is this?
I'm stumped. How can I define the undefined generics in my array of types?
我被难住了。如何在类型数组中定义未定义的泛型?
As requested by those in the comments, here is a test case:
应评论中的那些人的要求,下面是一个测试案例:
using System.Reflection;
using static System.Console;
MethodInfo methodInfo = ((Delegate)MyTest<object, object>).Method.GetGenericMethodDefinition();
Type[] genericArguments = new Type[] { typeof(int), typeof(string) };
MethodInfo invokableMethodInfo = methodInfo.MakeGenericMethod(genericArguments);
WriteLine("----Method Invoke Result:");
invokableMethodInfo.Invoke(null, new object?[] { "hi" });
WriteLine("----End");
Type[] types = (
from ParameterInfo parameter
in methodInfo.GetParameters()
select parameter.ParameterType
).ToArray();
WriteLine();
WriteLine("----Undefined Generic Types:");
foreach (Type type in types)
WriteLine(type);
WriteLine("----End");
types = (
from ParameterInfo parameter
in invokableMethodInfo.GetParameters()
select parameter.ParameterType
).ToArray();
WriteLine();
WriteLine("----Defined Generic Types:");
foreach (Type type in types)
WriteLine(type);
WriteLine("----End");
ReadKey(true);
static void MyTest<TOne, TTwo>(TTwo Value)
{
WriteLine(typeof(TOne).ToString());
WriteLine(Value?.ToString());
}
It will output:
它将输出:
----Method Invoke Result:
System.Int32
hi
----End
----Undefined Generic Types:
TTwo
----End
----Defined Generic Types:
System.String
----End
In my code, I use the invokableMethodInfo
(calculated from the original methodInfo
) to get the Defined Generic Types
array. I would like to instead use the array of types from the methodInfo
to get the same result.
在我的代码中,我使用invokableMethodInfo(根据原始方法信息计算)来获取已定义的泛型类型数组。我想改用方法信息中的类型数组来获得相同的结果。
Any help would be greatly appreciated!
如有任何帮助,将不胜感激!
更多回答
How about getting the types
after you do MakeGenericMethod
?
在你做了MakeGenericMethod之后获取类型怎么样?
Can you show a concrete example of methodInfo
, genericArguments
, and the value of types
that you want to get as output?
您能展示一个方法信息、GenericArguments和您想要作为输出获得的类型的值的具体示例吗?
@Sweeper 1. My question assumed that the methodInfo
object was disposed of after creating the array of Type
s. 2. I'll see what I can do.
@Sweeper 1.我的问题假定方法Info对象在创建类型数组之后被释放。2.我看看我能做些什么。
@Sweeper Edited one in just now.
@Sweeper刚刚在里面编辑了一个。
Thank you. That is much clearer. Is there a typo in the last paragraph? Did you mean "I use the invokableMethodInfo
to get the Defined Generic Types array"?
谢谢。这一点要清楚得多。最后一段有没有打字错误?您的意思是“我使用invokableMethodInfo来获取已定义的泛型类型数组”?
Not sure why you can't/don't want to use invokableMethodInfo
to get the "filled in" parameter types. You should definitely do that if you can.
不确定为什么不能/不想使用invokableMethodInfo来获取“填充的”参数类型。如果可以的话,你绝对应该这么做。
Otherwise, you would have to do the "type replacing" yourself. You can find whether a parameter type is a type parameter by using IsGenericMethodParameter
, and get which type parameter it is by using GenericParameterPosition
. The index returned by GenericParameterPosition
can be directly used to access your genericArguments
array.
否则,您将不得不自己进行“类型替换”。您可以通过使用IsGenericMethodParameter来确定参数类型是否为类型参数,并通过使用Generic参数位置来获取它是哪种类型的参数。Generic参数位置返回的索引可直接用于访问GenericArguments数组。
The idea is basically, for each parameter of the method,
这个想法基本上是,对于方法的每个参数,
- if it is a type parameter, replace it with
genericArguments[GenericParameterPosition]
- if it is a generic type, recursively replace each of its type arguments with this algorithm
- if it is an array type, recursively replace the array element type
- if it is an byref type, recursively replace the byref element type
- if it is an pointer type, recursively replace the pointer element type
- otherwise, don't change it
// perhaps the name of this could be better...
private static Type AssignConcreteTypesToGenericTypeParameter(Type parameter, Type[] arguments) {
if (parameter.IsGenericMethodParameter)
return arguments[parameter.GenericParameterPosition];
if (parameter.IsGenericType)
return
parameter
.GetGenericTypeDefinition()
.MakeGenericType((
from Type x in ParameterType.GenericTypeArguments
select _AssignGenericsToParameterType(x, parameter))
.ToArray());
if (parameter.IsArray)
return
_AssignGenericsToParameterType(parameter.GetElementType()!, parameter)
.MakeArrayType(parameter.GetArrayRank());
if (parameter.IsByRef)
return
_AssignGenericsToParameterType(parameter.GetElementType()!, parameter)
.MakeByRefType();
if (parameter.IsPointer)
return
_AssignGenericsToParameterType(parameter.GetElementType()!, parameter)
.MakePointerType();
return parameter;
}
Example usage:
用法示例:
MethodInfo methodInfo = ((Delegate)MyTest<object, object>).Method.GetGenericMethodDefinition();
Type[] arguments = new[] { typeof(string), typeof(int) };
var actualParameterTypes = methodInfo.GetParameters().Select(
x => AssignConcreteTypesToGenericTypeParameter(x.ParameterType, arguments)
);
foreach (var t in actualParameterTypes)
Console.WriteLine(t);
Bear in mind, that there may be some edge cases that this doesn't handle. The .NET system is rather complicated. I've tried to handle all I could think of (arrays, references, pointers, et cetera), but a few might have slipped through. At any rate, such edge cases will probably be easy to correct when encountered.
请记住,这可能不能处理某些边缘情况。.NET系统相当复杂。我试着处理我能想到的所有东西(数组、引用、指针等),但可能有几个漏掉了。无论如何,当遇到这种边缘情况时,可能很容易纠正。
Happy coding!
编码快乐!
更多回答
我是一名优秀的程序员,十分优秀!