通过反射找到合适的构造函数?

问题描述 投票:0回答:2

在根据传递的参数找到正确的构造函数时,我遇到了一些反射问题。

到目前为止,当传递的构造函数参数不是

null
:

时,这段代码工作正常
public static ConstructorInfo? FindSuitableConstructor(Type typeWithConstructorToFind, object?[] constructorArguments)
{
    return typeWithConstructorToFind.GetConstructor(
            constructorArguments.Select(constructorArgumentInstance => constructorArgumentInstance.GetType())
            .ToArray());
}

但是一旦涉及

null
,它就会崩溃,因为它无法在
GetType()
上调用
null

有哪些选择可以找到合适的构造函数?我也在考虑检查参数的数量和参数类型(但同样,它不适用于

null
),但到目前为止还没有接近我想要的。

我知道

Activator.CreateInstance(typeWithConstructorToFind, constructorArguments)
可以以某种方式找到正确的构造函数,但我不需要该类型的实例,我真的需要
ConstructorInfo

c# .net constructor system.reflection
2个回答
4
投票

你可以尝试使用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;
    }
}

它将根据传递的参数在一组方法(在本例中为构造函数)中选择最佳匹配。这比尝试自己做要好,因为有一些微妙的情况。请注意,如果有多个构造函数与您的参数匹配,则不一定会失败。例如:

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

    }

    public Test(long x, object y) {

    }
}

如果我们尝试:

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

理论上两者都匹配,它将返回第一个带有

string
参数的构造函数,因为你确实可以这样做:

new Test(1, null);

编译器将选择

string
重载。但是,如果您有这样的例子:

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

    }

    public Test(long x, MethodInfo y) {

    }
}

然后,

AmbiguousMatchException
同样会失败,因为实际上无法选择(并且在这种情况下
new Test(1, null)
将无法编译)。


2
投票

不确定是否有内置方法可以实现此目的。但您始终可以尝试手动查找构造函数。开始的事情:

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;
© www.soinside.com 2019 - 2024. All rights reserved.