dotnet / dotnet/dotnet-api-docs
Module.ResolveMethod Method is not clear about which module to resolve from.
- Dominant language
- C#
- Stars
- 949
- Forks
- 1.7k
- Avg merge
- 3d 27m
- Merged PRs (30d)
- 49
Description
docs: https://docs.microsoft.com/dotnet/api/system.reflection.module.resolvemethod
GitHub: https://github.com/dotnet/dotnet-api-docs/blob/master/xml/System.Reflection/Module.xml
In the example it's not clear which module should be used to resolve the method. Is it the module of the caller method or the module of the called method.
I have a sample where each class is in its own module/assembly and I have 3 more use cases:
#### G3.dll
``` CSharp
using System;
namespace ResolveMethodExample
{
public static class G3
{
public static void GM3(Tg3_1 p1, Tg3_2 p2)
{
// Case 6: A generic method call with open generics that depends on its generic
// context, because it uses the type parameters of the enclosing
// generic method GM3. The token
// for the MethodSpec is Tokens.Case6.
G4.GM4(p1, p2);
// Case 7: A generic method call with closed and open generics that depends on its generic
// context, because it uses the type parameters of the enclosing
// generic method GM3. The token
// for the MethodSpec is Tokens.Case7.
G4.GM4(p1, p2);
// Case 8: A generic method call with closed and open generics that depends on its generic
// context, because it uses the type parameters of the enclosing
// generic method GM3. The token
// for the MethodSpec is Tokens.Case8.
G4.GM4(p1, p2);
}
}
}
```
#### G4.dll
``` CSharp
using System;
namespace ResolveMethodExample
{
public static class G4
{
public static void GM4(Tg4 p1, Tgm4 p2)
{
throw new NotImplementedException();
}
}
}
```
I've also tweaked the output of the example application:
#### Example.exe
``` CSharp
class Program
{
static void Main(string[] args)
{
Module g2Module = typeof(G2<>).Assembly.ManifestModule;
Module g3Module = typeof(G3).Assembly.ManifestModule;
Module ngModule = typeof(NG).Assembly.ManifestModule;
MethodInfo miResolved2;
// Case 1: A generic method call that is dependent on its generic context.
//
// Create and display a MethodInfo representing the MethodSpec of the
// generic method g.GM1() that is called in G2.GM2().
Type t = typeof(G1<>).MakeGenericType(typeof(G2<>).GetGenericArguments());
MethodInfo mi = typeof(G2<>).GetMethod(nameof(G2.GM2));
MethodInfo miTest = t.GetMethod(nameof(G1.GM1)).MakeGenericMethod(mi.GetGenericArguments());
Console.WriteLine(@"
Case 1: A generic method call that is dependent on its generic context.
Constructed method: 0x{0:X8} {1}.{2}", miTest.MetadataToken, miTest.DeclaringType, miTest);
// Resolve the MethodSpec token for method G1.GM1(), which
// is called in method G2.GM2(). The GetGenericArguments method
// must be used to obtain the context for resolving the method.
MethodInfo miResolved = (MethodInfo)g2Module.ResolveMethod(
Tokens.Case1,
typeof(G2<>).GetGenericArguments(),
typeof(G2<>).GetMethod(nameof(G2.GM2)).GetGenericArguments());
Console.WriteLine(@"Token to resolve: 0x{0:X8}
Resolved method from generic context: 0x{1:X8} {2}.{3}
Is the resolved method the same? {4}", Tokens.Case1, miResolved.MetadataToken, miResolved.DeclaringType, miResolved, miResolved == miTest);
// The overload that doesn't specify generic context throws an exception
// because there is insufficient context to resolve the token.
try
{
miResolved2 = (MethodInfo)g2Module.ResolveMethod(Tokens.Case1);
}
catch (Exception ex)
{
Console.WriteLine(@"Resolved method: {0}: {1}", ex.GetType(), ex.Message);
}
// Case 2: A non-generic method call that is dependent on its generic context.
//
// Create and display a MethodInfo representing the MemberRef of the
// non-generic method g.M1() that is called in G2.GM2().
t = typeof(G1<>).MakeGenericType(typeof(G2<>).GetGenericArguments());
miTest = t.GetMethod(nameof(G1.M1));
Console.WriteLine(@"
Case 2: A non-generic method call that is dependent on its generic context.
Constructed method: 0x{0:X8} {1}.{2}", miTest.MetadataToken, miTest.DeclaringType, miTest);
// Resolve the MemberRef token for method G1.M1(), which is
// called in method G2.GM2(). The GetGenericArguments method
// must be used to obtain the context for resolving the method, because
// the method parameter comes from the generic type G1, and the type
// argument, Tg2, comes from the generic type that encloses the call.
// There is no enclosing generic method, so the value Type.EmptyTypes
// could be passed for the genericMethodArguments parameter.
miResolved = (MethodInfo)g2Module.ResolveMethod(
Tokens.Case2,
typeof(G2<>).GetGenericArguments(),
typeof(G2<>).GetMethod(nameof(G2.GM2)).GetGenericArguments());
Console.WriteLine(@"Token to resolve: 0x{0:X8}
Resolved method from generic context: 0x{1:X8} {2}.{3}
Is the resolved method the same? {4}", Tokens.Case2, miResolved.MetadataToken, miResolved.DeclaringType, miResolved, miResolved == miTest);
// The overload that doesn't specify generic context throws an exception
// because there is insufficient context to resolve the token.
try
{
miResolved2 = (MethodInfo)g2Module.ResolveMethod(Tokens.Case2);
}
catch (Exception ex)
{
Console.WriteLine(@"Resolved method: {0}: {1}", ex.GetType(), ex.Message);
}
// Case 3: A generic method call that is independent of its generic context.
//
// Create and display a MethodInfo representing the MethodSpec of the
// generic method gi.GM1() that is called in G2.GM2().
mi = typeof(G1).GetMethod(nameof(G1.GM1));
miTest = mi.MakeGenericMethod(new Type[] { typeof(object) });
Console.WriteLine(@"
Case 3: A generic method call that is independent of its generic context.
0x{0:X8} {1}.{2}", miTest.MetadataToken, miTest.DeclaringType, miTest);
// Resolve the token for method G1.GM1(), which is called
// in G2.GM2(). The GetGenericArguments method is used to
// obtain the context for resolving the method, but the method call in
// this case does not use type parameters of the enclosing type or
// method, so Type.EmptyTypes could be used for both arguments.
miResolved = (MethodInfo)g2Module.ResolveMethod(
Tokens.Case3,
typeof(G2<>).GetGenericArguments(),
typeof(G2<>).GetMethod(nameof(G2.GM2)).GetGenericArguments());
Console.WriteLine(@"Token to resolve: 0x{0:X8}
Resolved method from generic context: 0x{1:X8} {2}.{3}
Is the resolved method the same? {4}", Tokens.Case3, miResolved.MetadataToken, miResolved.DeclaringType, miResolved, miResolved == miTest);
// The method call in this case does not depend on the enclosing generic
// context, so the token can also be resolved by the simpler overload.
miResolved2 = (MethodInfo)g2Module.ResolveMethod(Tokens.Case3);
Console.WriteLine(@"Resolved method: 0x{0:X8} {1}.{2}", miResolved2.MetadataToken, miResolved2.DeclaringType, miResolved2);
// Case 4: A non-generic method call that is independent of its generic context.
//
// Create and display a MethodInfo representing the MethodDef of the
// method e.M() that is called in G2.GM2().
miTest = typeof(NG).GetMethod(nameof(NG.M));
Console.WriteLine(@"
Case 4: A non-generic method call that is independent of its generic context.
Constructed method: 0x{0:X8} {1}.{2}", miTest.MetadataToken, miTest.DeclaringType, miTest);
// Resolve the token for method NG.M(), which is called in
// G2.GM2(). The GetGenericArguments method is used to
// obtain the context for resolving the method, but the non-generic
// method call does not use type parameters of the enclosing type or
// method, so Type.EmptyTypes could be used for both arguments.
miResolved = (MethodInfo)g2Module.ResolveMethod(
Tokens.Case4,
typeof(G2<>).GetGenericArguments(),
typeof(G2<>).GetMethod(nameof(G2.GM2)).GetGenericArguments());
Console.WriteLine(@"Token to resolve: 0x{0:X8}
Resolved method from generic context: 0x{1:X8} {2}.{3}
Is the resolved method the same? {4}", Tokens.Case4, miResolved.MetadataToken, miResolved.DeclaringType, miResolved, miResolved == miTest);
// The method call in this case does not depend on any enclosing generic
// context, so the token can also be resolved by the simpler overload.
miResolved2 = (MethodInfo)g2Module.ResolveMethod(Tokens.Case4);
Console.WriteLine(@"Resolved method: 0x{0:X8} {1}.{2}", miResolved2.MetadataToken, miResolved2.DeclaringType, miResolved2);
// Case 5: Generic method call in a non-generic context.
//
// Create and display a MethodInfo representing the MethodRef of the
// closed generic method g.GM1() that is called in NG.M().
mi = typeof(G1).GetMethod(nameof(G1.GM1));
miTest = mi.MakeGenericMethod(new Type[] { typeof(object) });
Console.WriteLine(@"
Case 5: Generic method call in a non-generic context.
Constructed method: 0x{0:X8} {1}.{2}", miTest.MetadataToken, miTest.DeclaringType, miTest);
// Resolve the token for method G1.GM1(), which is called
// in method NG.M(). The GetGenericArguments method is used to
// obtain the context for resolving the method, but the enclosing type
// and method are not generic, so Type.EmptyTypes could be used for
// both arguments.
miResolved = (MethodInfo)ngModule.ResolveMethod(
Tokens.Case5,
typeof(NG).GetGenericArguments(),
typeof(NG).GetMethod(nameof(NG.M)).GetGenericArguments());
Console.WriteLine(@"Token to resolve: 0x{0:X8}
Resolved method from generic context: 0x{1:X8} {2}.{3}
Is the resolved method the same? {4}", Tokens.Case5, miResolved.MetadataToken, miResolved.DeclaringType, miResolved, miResolved == miTest);
// The method call in this case does not depend on any enclosing generic
// context, so the token can also be resolved by the simpler overload.
miResolved2 = (MethodInfo)ngModule.ResolveMethod(Tokens.Case5);
Console.WriteLine(@"Resolved method: 0x{0:X8} {1}.{2}", miResolved2.MetadataToken, miResolved2.DeclaringType, miResolved2);
// Case 6: A generic method call with open generics that is dependent on its generic context.
//
// Create and display a MethodInfo representing the MemberRef of the
// generic method G4.GM4() that is called in G3.GM3().
miTest = typeof(G4<>).MakeGenericType(typeof(G3).GetMethod(nameof(G3.GM3)).GetGenericArguments()[0])
.GetMethod(nameof(G4.GM4)).MakeGenericMethod(typeof(G3).GetMethod(nameof(G3.GM3)).GetGenericArguments()[1]);
Console.WriteLine(@"
Case 6: A generic method call with open generics that is dependent on its generic context.
Constructed method: 0x{0:X8} {1}.{2}", miTest.MetadataToken, miTest.DeclaringType, miTest);
// Resolve the MethodSpec token for method G4.GM4(), which
// is called in method G3.GM3(). The GetGenericArguments method
// must be used to obtain the context for resolving the method.
miResolved = (MethodInfo)g3Module.ResolveMethod(
Tokens.Case6,
typeof(G3).GetGenericArguments(),
typeof(G3).GetMethod(nameof(G3.GM3)).GetGenericArguments());
Console.WriteLine(@"Token to resolve: 0x{0:X8}
Resolved method from generic context: 0x{1:X8} {2}.{3}
Is the resolved method the same? {4}", Tokens.Case6, miResolved.MetadataToken, miResolved.DeclaringType, miResolved, miResolved == miTest);
// The overload that doesn't specify generic context throws an exception
// because there is insufficient context to resolve the token.
try
{
miResolved2 = (MethodInfo)g3Module.ResolveMethod(Tokens.Case6);
}
catch (Exception ex)
{
Console.WriteLine(@"Resolved method: {0}: {1}", ex.GetType(), ex.Message);
}
// Case 7: A generic method call with closed and open generics that depends on its generic context.
//
// Create and display a MethodInfo representing the MemberRef of the
// generic method G4.GM4() that is called in G3.GM3().
miTest = typeof(G4<>).MakeGenericType(typeof(object))
.GetMethod(nameof(G4.GM4)).MakeGenericMethod(typeof(G3).GetMethod(nameof(G3.GM3)).GetGenericArguments()[1]);
Console.WriteLine(@"
Case 7: A generic method call with closed and open generics that depends on its generic context.
Constructed method: 0x{0:X8} {1}.{2}", miTest.MetadataToken, miTest.DeclaringType, miTest);
// Resolve the MethodSpec token for method G4.GM4(), which
// is called in method G3.GM3(). The GetGenericArguments method
// must be used to obtain the context for resolving the method.
miResolved = (MethodInfo)g3Module.ResolveMethod(
Tokens.Case7,
typeof(G3).GetGenericArguments(),
typeof(G3).GetMethod(nameof(G3.GM3)).GetGenericArguments());
Console.WriteLine(@"Token to resolve: 0x{0:X8}
Resolved method from generic context: 0x{1:X8} {2}.{3}
Is the resolved method the same? {4}", Tokens.Case7, miResolved.MetadataToken, miResolved.DeclaringType, miResolved, miResolved == miTest);
// The overload that doesn't specify generic context throws an exception
// because there is insufficient context to resolve the token.
try
{
miResolved2 = (MethodInfo)g3Module.ResolveMethod(Tokens.Case6);
}
catch (Exception ex)
{
Console.WriteLine(@"Resolved method: {0}: {1}", ex.GetType(), ex.Message);
}
// Case 8: A generic method call with closed and open generics that depends on its generic context.
//
// Create and display a MethodInfo representing the MemberRef of the
// generic method G4.GM4() that is called in G3.GM3().
miTest = typeof(G4<>).MakeGenericType(typeof(G3).GetMethod(nameof(G3.GM3)).GetGenericArguments()[0])
.GetMethod(nameof(G4.GM4)).MakeGenericMethod(typeof(object));
Console.WriteLine(@"
Case 8: A generic method call with closed and open generics that depends on its generic context.
Constructed method: 0x{0:X8} {1}.{2}", miTest.MetadataToken, miTest.DeclaringType, miTest);
// Resolve the MethodSpec token for method G4.GM4(), which
// is called in method G3.GM3(). The GetGenericArguments method
// must be used to obtain the context for resolving the method.
miResolved = (MethodInfo)g3Module.ResolveMethod(
Tokens.Case8,
typeof(G3).GetGenericArguments(),
typeof(G3).GetMethod(nameof(G3.GM3)).GetGenericArguments());
Console.WriteLine(@"Token to resolve: 0x{0:X8}
Resolved method from generic context: 0x{1:X8} {2}.{3}
Is the resolved method the same? {4}", Tokens.Case8, miResolved.MetadataToken, miResolved.DeclaringType, miResolved, miResolved == miTest);
// The overload that doesn't specify generic context throws an exception
// because there is insufficient context to resolve the token.
try
{
miResolved2 = (MethodInfo)g3Module.ResolveMethod(Tokens.Case7);
}
catch (Exception ex)
{
Console.WriteLine(@"Resolved method: {0}: {1}", ex.GetType(), ex.Message);
}
}
}
}
/*
This example produces the following output:
Case 1: A generic method call that is dependent on its generic context.
Constructed method: 0x06000001 ResolveMethodExample.G1`1[Tg2].Void GM1[Tgm2](Tg2, Tgm2)
Token to resolve: 0x2B000001
Resolved method from generic context: 0x06000001 ResolveMethodExample.G1`1[Tg2].Void GM1[Tgm2](Tg2, Tgm2)
Is the resolved method the same? True
Resolved method: System.ArgumentException: A BadImageFormatException has been thrown while parsing the signature. This is likely due to lack of a generic context. Ensure genericTypeArguments and genericMethodArguments are provided and contain enough context.
Case 2: A non-generic method call that is dependent on its generic context.
Constructed method: 0x06000002 ResolveMethodExample.G1`1[Tg2].Void M1(Tg2)
Token to resolve: 0x0A00000D
Resolved method from generic context: 0x06000002 ResolveMethodExample.G1`1[Tg2].Void M1(Tg2)
Is the resolved method the same? True
Resolved method: System.ArgumentException: A BadImageFormatException has been thrown while parsing the signature. This is likely due to lack of a generic context. Ensure genericTypeArguments and genericMethodArguments are provided and contain enough context.
Case 3: A generic method call that is independent of its generic context.
0x06000001 ResolveMethodExample.G1`1[System.Int32].Void GM1[Object](Int32, System.Object)
Token to resolve: 0x2B000002
Resolved method from generic context: 0x06000001 ResolveMethodExample.G1`1[System.Int32].Void GM1[Object](Int32, System.Object)
Is the resolved method the same? True
Resolved method: 0x06000001 ResolveMethodExample.G1`1[System.Int32].Void GM1[Object](Int32, System.Object)
Case 4: A non-generic method call that is independent of its generic context.
Constructed method: 0x06000001 ResolveMethodExample.NG.Void M()
Token to resolve: 0x0A000012
Resolved method from generic context: 0x06000001 ResolveMethodExample.NG.Void M()
Is the resolved method the same? True
Resolved method: 0x06000001 ResolveMethodExample.NG.Void M()
Case 5: Generic method call in a non-generic context.
Constructed method: 0x06000001 ResolveMethodExample.G1`1[System.Int32].Void GM1[Object](Int32, System.Object)
Token to resolve: 0x2B000001
Resolved method from generic context: 0x06000001 ResolveMethodExample.G1`1[System.Int32].Void GM1[Object](Int32, System.Object)
Is the resolved method the same? True
Resolved method: 0x06000001 ResolveMethodExample.G1`1[System.Int32].Void GM1[Object](Int32, System.Object)
Case 6: A generic method call with open generics that is dependent on its generic context.
Constructed method: 0x06000001 ResolveMethodExample.G4`1[Tg3_1].Void GM4[Tg3_2](Tg3_1, Tg3_2)
Token to resolve: 0x2B000001
Resolved method from generic context: 0x06000001 ResolveMethodExample.G4`1[Tg3_1].Void GM4[Tg3_2](Tg3_1, Tg3_2)
Is the resolved method the same? True
Resolved method: System.ArgumentException: A BadImageFormatException has been thrown while parsing the signature. This is likely due to lack of a generic context. Ensure genericTypeArguments and genericMethodArguments are provided and contain enough context.
Case 7: A generic method call with closed and open generics that depends on its generic context.
Constructed method: 0x06000001 ResolveMethodExample.G4`1[System.Object].Void GM4[Tg3_2](System.Object, Tg3_2)
Token to resolve: 0x2B000002
Resolved method from generic context: 0x06000001 ResolveMethodExample.G4`1[System.Object].Void GM4[Tg3_2](System.Object, Tg3_2)
Is the resolved method the same? True
Resolved method: System.ArgumentException: A BadImageFormatException has been thrown while parsing the signature. This is likely due to lack of a generic context. Ensure genericTypeArguments and genericMethodArguments are provided and contain enough context.
Case 8: A generic method call with closed and open generics that depends on its generic context.
Constructed method: 0x06000001 ResolveMethodExample.G4`1[Tg3_1].Void GM4[Object](Tg3_1, System.Object)
Token to resolve: 0x2B000003
Resolved method from generic context: 0x06000001 ResolveMethodExample.G4`1[Tg3_1].Void GM4[Object](Tg3_1, System.Object)
Is the resolved method the same? True
Resolved method: System.ArgumentException: A BadImageFormatException has been thrown while parsing the signature. This is likely due to lack of a generic context. Ensure genericTypeArguments and genericMethodArguments are provided and contain enough context.
*/
```
I also wrote something to get the method metadata tokens at run-time:
```
// Metadata tokens for the MethodRefs that are to be resolved.
static class Tokens
{
public static readonly int Case1 = GetCalledMetadataToken(typeof(G2<>).GetMethod(nameof(G2.GM2)), 0); // g.GM1(param1, param2);
public static readonly int Case2 = GetCalledMetadataToken(typeof(G2<>).GetMethod(nameof(G2.GM2)), 1); // g.M1(param1);
public static readonly int Case3 = GetCalledMetadataToken(typeof(G2<>).GetMethod(nameof(G2.GM2)), 2); // gi.GM1(42, new Object());
public static readonly int Case4 = GetCalledMetadataToken(typeof(G2<>).GetMethod(nameof(G2.GM2)), 3); // e.M();
public static readonly int Case5 = GetCalledMetadataToken(typeof(NG).GetMethod(nameof(NG.M)), 0); // g.GM1(42, new Object());
public static readonly int Case6 = GetCalledMetadataToken(typeof(G3).GetMethod(nameof(G3.GM3)), 0); // G4.GM(p1, p2);
public static readonly int Case7 = GetCalledMetadataToken(typeof(G3).GetMethod(nameof(G3.GM3)), 1); // G4.GM(p1, p2);
public static readonly int Case8 = GetCalledMetadataToken(typeof(G3).GetMethod(nameof(G3.GM3)), 2); // G4.GM(p1, p2);
static int GetCalledMetadataToken(MethodInfo mi, int idx)
{
var il = mi.GetMethodBody().GetILAsByteArray();
var ip = 0;
for (var i = 0; i <= idx; i++)
{
while (il[ip] != OpCodes.Callvirt.Value && il[ip] != OpCodes.Call.Value)
{
ip++;
}
ip++;
}
return BitConverter.ToInt32(il, ip);
}
}
```
Contributor guide
Assessment
This issue has not been assessed yet.