在根据传递的参数找到正确的构造函数时,我遇到了一些反射问题。
到目前为止,当传递的构造函数参数不是
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
。
你可以尝试使用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)
将无法编译)。
不确定是否有内置方法可以实现此目的。但您始终可以尝试手动查找构造函数。开始的事情:
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;