Home > Net >  Find suitable Constructor with reflection?
Find suitable Constructor with reflection?

Time:01-10

I am having some reflection issues, when finding the right constructor depending on the passed parameters.

So far this piece of code works fine, when the constructor arguments that are passed are not null:

public static ConstructorInfo? FindSuitableConstructor(Type typeWithConstructorToFind, object?[] constructorArguments)
{
    return typeWithConstructorToFind.GetConstructor(
            constructorArguments.Select(constructorArgumentInstance => onstructorArgumentInstance.GetType())
            .ToArray());
}

But as soon as there is null involved it crashes as it can not call GetType() on null.

What are the alternatives to find the suitable constructor? I was thinking also of checking the numbers of arguments and also the parameter types (but again, it does not work on null) but so far not closer to what I am looking for.

I know that Activator.CreateInstance(typeWithConstructorToFind, constructorArguments) can find the right constructor somehow, but I don't need the instace of that type, I really need the ConstructorInfo.

CodePudding user response:

You can try to use Binder.BindToMethod:

public static ConstructorInfo? FindSuitableConstructor(Type typeWithConstructorToFind, object[] constructorArguments) {
    if (constructorArguments == null)
        throw new ArgumentNullException("constructorArguments");
    var constructors = typeWithConstructorToFind.GetConstructors();
    var argsCopy = constructorArguments.ToArray();
    try {
        return (ConstructorInfo)Type.DefaultBinder.BindToMethod(BindingFlags.Instance | BindingFlags.Public, constructors, ref argsCopy, null, null, null, out _);
    }
    catch (MissingMemberException) {
        return null;
    }
}

It will select the best match among a set of methods (in this case constructors) according to passed arguments. It's better than trying to do that yourself, because there are subtle cases. Note that it will not necessary fail if there are several constructors which match your arguments. For example:

public class Test {
    public Test(long x, string y) {

    }

    public Test(long x, object y) {

    }
}

If we try:

FindSuitableConstructor(typeof(Test), new object[] { 1, null });

which in theory matches both, it will return first constructor with string argument, because indeed you can do:

new Test(1, null);

And compiler will choose the string overload. However if you have for example this:

public class Test {
    public Test(long x, string y) {

    }

    public Test(long x, MethodInfo y) {

    }
}

Then the same will fail with AmbiguousMatchException, because indeed it's not possible to choose (and new Test(1, null) will not compile in this case).

CodePudding user response:

Not sure there is a build in method to allow achieve this. But you can an always try finding constructor manually. Something to start with:

var objects = new object?[] { 1, null };
if (objects.All(o => o is not null))
{
    return ..; // use current solution
}

var types = objects.Select(o => o?.GetType()).ToArray();
ConstructorInfo? candidate = null;
foreach (var constructorInfo in typeWithConstructorToFind.GetConstructors())
{
    var @params = constructorInfo.GetParameters();
    if (@params.Length != types.Length)
    {
        continue;
    }

    for (var index = 0; index < @params.Length; index  )
    {
        var parameterInfo = @params[index];
        var type = types[index];
        if ((type == null && !parameterInfo.ParameterType.IsValueType) || parameterInfo.ParameterType.IsAssignableFrom(type)) // todo - check for explicit casts
        {
            continue; // can pass null or type matches - continue params check
        }
        break;
    }
    
    // all params satisfy 
    if (candidate is null)
    {
        candidate = constructorInfo;
    }
    else
    {
        throw new AmbiguousMatchException();
    }
}    

return candidate;
  • Related