Get Generic Constructor From Closed Version (Net Standard 1.1)

Viewed 263

I'll start by linking this question which shows one way to do what I want to do - unfortunately it's not possible in .Net Standard 1.1.

Although I need this to work for any generic type, here's an example type:

class MyGeneric<T>
{
   public MyGeneric(T a1, string a2) { }
   public MyGeneric(T a1, double a2) { }
}

I want a function which, when supplied with a ConstructorInfo for a concrete MyGeneric<>, for example:

  • MyGeneric<int>..ctor(int, double)

Will return another ConstructorInfo for the same constructor, but for the unbound generic type MyGeneric<> instead:

  • MyGeneric<T>..ctor(T, double)

The purpose is then to be able to go the other way - i.e. given MyGeneric<T>..ctor(T, double), I can get the same constructor for any bound/closed version of that generic - e.g. MyGeneric<DateTime>..ctor(DateTime, double), or MyGeneric<Foo>..ctor(Foo, double).

It must also work when the generic type parameters appear as type arguments to another generic in the constructor signature - e.g. MyGeneric(IEnumerable<T>)

The best implementation I can find is the one from the link above, which uses MethodBase.GetMethodFromHandle(RuntimeMethodHandle, RuntimeTypeHandle):

return (ConstructorInfo)MethodBase.GetMethodFromHandle(
           ctor.MethodHandle, 
           ctor.DeclaringType.GetGenericTypeDefinition().TypeHandle);

(assumes ctor contains a reference to the concrete generic type's version of the constructor)

This works for all constructors on all types, regardless of how wacky the relationship between the generic type's own type parameters and the types of the constructor parameters.

However, whilst .netstandard1.1 does have the MethodBase.GetMethodFromHandle static method, there doesn't appear to be a way to get the RuntimeMethodHandle from a MethodInfo object (i.e. no MethodHandle property on the MethodBase base class of Constructorinfo).

So it looks to me like it's impossible to do it like this.

Current implementation for .Net Standard 1.1

Currently, my implementation for .Net Standard 1.1 assumes that the order of members returned by the Type (or TypeInfo) for an open generic will be the same as those returned for any closed generic built from it:

var position = Array.IndexOf(
    TypeHelpers.GetConstructors(ctor.DeclaringType),
    ctor);
if(position >= 0)
{
    return TypeHelpers.GetConstructors(
        ctor.DeclaringType.GetGenericTypeDefinition())[position];
}

Note: The TypeHelpers.GetConstructors method just returns public constructors for a type using either the old or newer reflection APIs depending on target framework

Once I know the index, I then reuse it to map back from the open generic's constructor to the closed one for any closed/bound generic built from that open generic.

This does appear to work (with caveats, see next), which kinda makes sense (since all concrete generics should logically 'inherit' all the field/method metadata lists in the same order) - but it feels wrong, because we should never rely on the order of the results returned from any of the reflection APIs.

Caveats The above TypeInfo-based solution works intermittently because it relies on ConstructorInfo being cached whenever the DeclaredConstructors API is used, so that you can compare reference equality for the constructors. But it turns out that this isn't always the case!

A better way?

Is there a better way? Is it possible to get RuntimeMethodHandle or RuntimeTypeHandle from a ConstructorInfo in .NetStandard 1.1 - or should I be consigned to searching arrays of methods/constructors as in the second code example?

It seems odd that .NetStandard 1.1 has the MethodBase.GetMethodFromHandle overloads, but doesn't seem to provide any way to get the handles that you need as parameters!

1 Answers

I am still on a learning curve for reflection in core but Take a look a the following example and see if it can help.

//assumes ctor contains a reference to the 
//concrete generic type's version of the constructor
public Type GetDeclaringType(ConstructorInfo ctor) {
    var type = ctor.DeclaringType;//Gets the class that declares this member.
    var typeInfo = type.GetTypeInfo();//Returns TypeInfo representation of specified type.
    if (typeInfo.IsGenericType) {
        type = typeInfo.GetGenericTypeDefinition();
    }
    return type;
}

So assuming ctor is MyGeneric<DateTime>..ctor(DateTime, double)

var declaredType = GetDeclaringType(ctor); //MyGeneric<>
var closedType = declaredType.MakeGenericType(typeof(int));//MyGeneric<int>
//constructors
var typeInfo = closedType.GetTypeInfo();
var constructors = typeInfo.GetConstructors();
//MyGeneric<int>..ctor(int, double)
var constructorWithDouble = constructors
    .Where(ctor => ctor.GetParameters().Skip(1).First().ParameterType == typeof(double))
    .First();
Related