Thanks. Unfortunately that didn't help because I need the
MethodDefinition of the original method, I can't just construct my own
reference to it.
I ended up doing it by finding the original generic type, going
through its methods and finding the one with the same name, number of
generic type parameters, parameter types and return type. I had to
write a recursive function to test the equality of the parameter types
and return type (which may themselves be generic instance types), but
so far it seems to work in various crazy cases I've tested. Does this
sound like a robust solution to you, or can you think of an obvious
drawback with this?
I've pasted the code below.
Thanks again!
Timwi
P.S. Of course you're free to use this code in Mono.Cecil if you like.
MethodDefinition FindOriginalMethod(MethodReference mr)
{
if (mr is MethodDefinition)
return (MethodDefinition) mr;
var declaringType = mr.DeclaringType;
if (declaringType is GenericInstanceType)
{
var realType = declaringType.GetElementType() as
TypeDefinition;
if (realType == null)
throw new InvalidOperationException("Expecting
DeclaringType to be a TypeDefinition.");
var genericParameters = realType.GenericParameters;
var genericArguments = ((GenericInstanceType)
declaringType).GenericArguments;
return realType.Methods.Single(m =>
m.Name == mr.Name &&
m.GenericParameters.Count == mr.GenericParameters.Count &&
parameterTypesMatch(m.Parameters, mr.Parameters,
genericParameters, genericArguments) &&
typeMatches(m.ReturnType, mr.ReturnType,
genericParameters, genericArguments)
);
}
throw new InvalidOperationException("Couldn't find the original
method.");
}
private static bool
parameterTypesMatch(Collection<ParameterDefinition>
parametersWithGenerics, Collection<ParameterDefinition>
concreteParameters, Collection<GenericParameter> genericParameters,
Collection<TypeReference> genericArguments)
{
if (parametersWithGenerics.Count != concreteParameters.Count)
return false;
return Enumerable.Range(0, parametersWithGenerics.Count).All(i =>
typeMatches(parametersWithGenerics[i].ParameterType,
concreteParameters[i].ParameterType, genericParameters,
genericArguments));
}
private static bool typeMatches(TypeReference typeWithGenerics,
TypeReference concreteType, Collection<GenericParameter>
genericParameters, Collection<TypeReference> genericArguments)
{
if (typeWithGenerics.Equals(concreteType))
return true;
if (typeWithGenerics is GenericParameter && concreteType is
GenericParameter && ((GenericParameter) typeWithGenerics).Position ==
((GenericParameter) concreteType).Position)
{
var typeWithGenOwner = ((GenericParameter)
typeWithGenerics).Owner;
var concreteTypeOwner = ((GenericParameter)
concreteType).Owner;
if (typeWithGenOwner is TypeReference && concreteTypeOwner is
TypeReference && ((TypeReference) concreteTypeOwner).GetElementType()
== typeWithGenOwner)
return true;
if (typeWithGenOwner is MethodReference && concreteTypeOwner
is MethodReference && ((MethodReference)
concreteTypeOwner).GetElementMethod() == typeWithGenOwner)
return true;
}
if (Enumerable.Range(0, genericParameters.Count).Any(i =>
typeWithGenerics.Equals(genericParameters[i]) &&
concreteType.Equals(genericArguments[i])))
return true;
if (!(typeWithGenerics is GenericInstanceType) || !(concreteType
is GenericInstanceType))
return false;
if (!
typeWithGenerics.GetElementType().Equals(concreteType.GetElementType()))
return false;
var argumentsWithGenerics = ((GenericInstanceType)
typeWithGenerics).GenericArguments;
var concreteArguments = ((GenericInstanceType)
concreteType).GenericArguments;
return Enumerable.Range(0, argumentsWithGenerics.Count).All(i =>
typeMatches(argumentsWithGenerics[i], concreteArguments[i],
genericParameters, genericArguments));
}
On May 6, 4:30 pm, Jb Evain <[email protected]> wrote:
> Hey,
>
> On Thu, May 6, 2010 at 3:39 AM, Timwi <[email protected]> wrote:
> > class SomeGenericType<T1, T2> {
> > void SomeMethod(T1 t1, T2 t2) { /* ... */ }
> > }
>
> > Now suppose I have a MethodReference that refers to a specific
> > instance of this, e.g.
>
> > SomeGenericType`2<string,int>::SomeMethod(!0,!1)
>
> > Is there an easy way to get the original method?
>
> > SomeGenericType`2::SomeMethod(T1,T2)
>
> There's no straightforward process. The declaring type will be a
> GenericInstanceType, so you may write something like:
>
> public static MethodReference GetOpenMethod (this
> MethodReference self)
> {
> var reference = new MethodReference {
> Name = self.Name,
> DeclaringType =
> self.DeclaringType.GetElementType (),
> HasThis = self.HasThis,
> ExplicitThis = self.ExplicitThis,
> ReturnType = self.ReturnType,
> CallingConvention =
> MethodCallingConvention.Generic,
> };
>
> foreach (var parameter in self.Parameters)
> reference.Parameters.Add (new
> ParameterDefinition
> (parameter.ParameterType));
>
> foreach (var generic_parameter in
> self.GenericParameters)
> reference.GenericParameters.Add (new
> GenericParameter (reference));
>
> return reference;
> }
>
> --
> Jb Evain <[email protected]>
>
> --
> --
> mono-cecil
--
--
mono-cecil