1

I have this interface:

interface IRepository
{
    string GetId<T>(T obj) where T : IDomainObject;
    string GetId<T>(Reference<T> reference) where T : IDomainObject;
}

What exactly IDomainObject and Reference are is not relevant to this question. So assume they are completely empty:

interface IDomainObject
{
    // something
}

class Reference<T> where T : IDomainObject
{
    // something
}

My question is: How do I get the MethodInfo for the GetId<T> method in IRepository that accepts a Reference<T>?


Here's what I've tried:

public MethodInfo GetReferenceAcceptingGetIdMethod()
{
    // We want to return the MethodInfo for the GetId<T> method of IRepository
    // that accepts a Reference<T> argument.

    var repositoryInterfaceType = typeof(IRepository);

    // Look through all methods of IRepository...
    foreach (var m in repositoryInterfaceType.GetMethods())
    {
        // ... to find a candidate method, going by Genericness and Name ...
        if (m.IsGenericMethodDefinition && m.Name == nameof(IRepository.GetId))
        {
            // ... and to narrow it further down by looking at the parameters ...
            var parameters = m.GetParameters();
            if (parameters.Length == 1)
            {
                // ... to check if the one and only parameter is a generic Reference<>.
                var firstParamType = parameters[0].ParameterType;
                var genericReferenceType = typeof(Reference<>);
                if (firstParamType == genericReferenceType)
                {
                    // !!! This if will never be true.
                    // Why?
                    // And what do I have to change the condition into to make it work?
                    return m;
                }
            }
        }
    }

    throw new Exception();
}

It appears that method's parameter type is somehow different from a completely open generic type. I guess and it seems that the type of the method's parameter is somehow linked to the method's generic type parameter.

So how can I get the MethodInfo in such a case?

Hauke P.
  • 2,695
  • 1
  • 20
  • 43

1 Answers1

2

The parameter type can't be the generic type definition. It has a type argument - which is the type parameter for the method. You can get that type parameter from MethodInfo.GetGenericArguments(), and then use it with typeof(Reference<>).MakeGenericType(...) to get the expected parameter type.

Here's a complete example, basically adapting your original code:

using System;
using System.Reflection;

class Reference<T> {}
interface IDomainObject {}

interface IRepository
{
    string GetId<T>(T obj) where T : IDomainObject;
    string GetId<T>(Reference<T> reference) where T : IDomainObject;
}

class Test
{
    static void Main()
    {
        var method = GetReferenceAcceptingGetIdMethod();
        Console.WriteLine(method);
    }

    public static MethodInfo GetReferenceAcceptingGetIdMethod()
    {
        var repositoryInterfaceType = typeof(IRepository);
        foreach (var m in repositoryInterfaceType.GetMethods())
        {
            if (m.IsGenericMethodDefinition && m.Name == nameof(IRepository.GetId))
            {
                var typeParameter = m.GetGenericArguments()[0];
                var expectedParameterType = typeof(Reference<>).MakeGenericType(typeParameter);
                var parameters = m.GetParameters();
                if (parameters.Length == 1)
                {
                    var firstParamType = parameters[0].ParameterType;
                    if (firstParamType == expectedParameterType)
                    {
                        return m;
                    }
                }
            }
        }        
        throw new Exception();
    }
}
Jon Skeet
  • 1,421,763
  • 867
  • 9,128
  • 9,194
  • Wow, cool. I did not expect that `m` would have a generic argument when it is still open. Also, the generic argument seems to be a pretty strange "Type" named `T`. But your solution works great - so thanks a lot. – Hauke P. Apr 04 '18 at 21:41
  • @HaukeP.: It's worth taking a step back to make sure you understand it before you include it in your code (so that you can change it later if necessary or write similar code in the future). It's not a strange type at all - it's the type parameter for the method. Look at how you've declared the parameter: `Reference reference`. That's *not* an open generic type; it's a generic type using a type argument which happens to be the type parameter from the method. – Jon Skeet Apr 04 '18 at 21:43