public override MethodBase BindToMethod(
BindingFlags bindingAttr, MethodBase[] canidates, ref Object[] args,
ParameterModifier[] modifiers, CultureInfo cultureInfo, String[] names, out Object state)
{
int i;
int j;
state = null;
if (canidates == null || canidates.Length == 0)
throw new ArgumentException(Environment.GetResourceString("Arg_EmptyArray"),"canidates");
#region Map named parameters to canidate parameter postions
// We are creating an paramOrder array to act as a mapping
// between the order of the args and the actual order of the
// parameters in the method. This order may differ because
// named parameters (names) may change the order. If names
// is not provided, then we assume the default mapping (0,1,...)
int[][] paramOrder = new int[canidates.Length][];
for (i = 0; i < canidates.Length; i++)
{
ParameterInfo[] par = canidates[i].GetParametersNoCopy();
// args.Length + 1 takes into account the possibility of a last paramArray that can be omitted
paramOrder[i] = new int[(par.Length > args.Length) ? par.Length : args.Length];
if (names == null)
{
// Default mapping
for (j = 0; j < args.Length; j++)
paramOrder[i][j] = j;
}
else
{
if (!CreateParamOrder(paramOrder[i], par, names))
canidates[i] = null;
}
}
#endregion
Type[] paramArrayTypes = new Type[canidates.Length];
Type[] argTypes = new Type[args.Length];
#region Cache the type of the provided arguments
// object that contain a null are treated as if they were typeless (but match either object
// references or value classes). We mark this condition by placing a null in the argTypes array.
for (i = 0; i < args.Length; i++)
{
if (args[i] != null)
{
argTypes[i] = args[i].GetType();
}
}
#endregion
// Find the method that matches...
int CurIdx = 0;
bool defaultValueBinding = ((bindingAttr & BindingFlags.OptionalParamBinding) != 0);
Type paramArrayType = null;
#region Filter methods by parameter count and type
for (i = 0; i < canidates.Length; i++)
{
paramArrayType = null;
// If we have named parameters then we may have a hole in the canidates array.
if (canidates[i] == null)
continue;
// Validate the parameters.
ParameterInfo[] par = canidates[i].GetParametersNoCopy();
#region Match method by parameter count
if (par.Length == 0)
{
#region No formal parameters
if (args.Length != 0)
{
if ((canidates[i].CallingConvention & CallingConventions.VarArgs) == 0)
continue;
}
// This is a valid routine so we move it up the canidates list.
paramOrder[CurIdx] = paramOrder[i];
canidates[CurIdx++] = canidates[i];
continue;
#endregion
}
else if (par.Length > args.Length)
{
#region Shortage of provided parameters
// If the number of parameters is greater than the number of args then
// we are in the situation were we may be using default values.
for (j = args.Length; j < par.Length - 1; j++)
{
if (par[j].DefaultValue == System.DBNull.Value)
break;
}
if (j != par.Length - 1)
continue;
if (par[j].DefaultValue == System.DBNull.Value)
{
if (!par[j].ParameterType.IsArray)
continue;
if (!par[j].IsDefined(typeof(ParamArrayAttribute), true))
continue;
paramArrayType = par[j].ParameterType.GetElementType();
}
#endregion
}
else if (par.Length < args.Length)
{
#region Excess provided parameters
// test for the ParamArray case
int lastArgPos = par.Length - 1;
if (!par[lastArgPos].ParameterType.IsArray)
continue;
if (!par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true))
continue;
if (paramOrder[i][lastArgPos] != lastArgPos)
continue;
paramArrayType = par[lastArgPos].ParameterType.GetElementType();
#endregion
}
else
{
#region Test for paramArray, save paramArray type
int lastArgPos = par.Length - 1;
if (par[lastArgPos].ParameterType.IsArray
&& par[lastArgPos].IsDefined(typeof(ParamArrayAttribute), true)
&& paramOrder[i][lastArgPos] == lastArgPos)
{
if (!par[lastArgPos].ParameterType.IsAssignableFrom(argTypes[lastArgPos]))
paramArrayType = par[lastArgPos].ParameterType.GetElementType();
}
#endregion
}
#endregion
Type pCls = null;
int argsToCheck = (paramArrayType != null) ? par.Length - 1 : args.Length;
#region Match method by parameter type
for (j = 0; j < argsToCheck; j++)
{
#region Classic argument coersion checks
// get the formal type
pCls = par[j].ParameterType;
if (pCls.IsByRef)
pCls = pCls.GetElementType();
// the type is the same
if (pCls == argTypes[paramOrder[i][j]])
continue;
// a default value is available
if (defaultValueBinding && args[paramOrder[i][j]] == Type.Missing)
continue;
// the argument was null, so it matches with everything
if (args[paramOrder[i][j]] == null)
continue;
// the type is Object, so it will match everything
if (pCls == typeof(Object))
continue;
// now do a "classic" type check
if (pCls.IsPrimitive)
{
if (argTypes[paramOrder[i][j]] == null || !CanConvertPrimitiveObjectToType(args[paramOrder[i][j]],(RuntimeType)pCls))
{
if (CanChangeType(args[paramOrder[i][j]],pCls,cultureInfo))
continue;
break;
}
}
else
{
if (argTypes[paramOrder[i][j]] == null)
continue;
if (!pCls.IsAssignableFrom(argTypes[paramOrder[i][j]]))
{
if (argTypes[paramOrder[i][j]].IsCOMObject)
{
if (pCls.IsInstanceOfType(args[paramOrder[i][j]]))
continue;
}
if (CanChangeType(args[paramOrder[i][j]],pCls,cultureInfo))
continue;
break;
}
}
#endregion
}
if (paramArrayType != null && j == par.Length - 1)
{
#region Check that excess arguments can be placed in the param array
for (; j < args.Length; j++)
{
if (paramArrayType.IsPrimitive)
{
if (argTypes[j] == null || !CanConvertPrimitiveObjectToType(args[j], (RuntimeType)paramArrayType))
break;
}
else
{
if (argTypes[j] == null)
continue;
if (!paramArrayType.IsAssignableFrom(argTypes[j]))
{
if (argTypes[j].IsCOMObject)
{
if (paramArrayType.IsInstanceOfType(args[j]))
continue;
}
break;
}
}
}
#endregion
}
#endregion
if (j == args.Length)
{
#region This is a valid routine so we move it up the canidates list
paramOrder[CurIdx] = paramOrder[i];
paramArrayTypes[CurIdx] = paramArrayType;
canidates[CurIdx++] = canidates[i];
#endregion
}
}
#endregion
// If we didn't find a method
if (CurIdx == 0)
throw new MissingMethodException(Environment.GetResourceString("MissingMember"));
if (CurIdx == 1)
{
#region Found only one method
if (names != null)
{
state = new BinderState((int[])paramOrder[0].Clone(), args.Length, paramArrayTypes[0] != null);
ReorderParams(paramOrder[0],args);
}
// If the parameters and the args are not the same length or there is a paramArray
// then we need to create a argument array.
ParameterInfo[] parms = canidates[0].GetParametersNoCopy();
if (parms.Length == args.Length)
{
if (paramArrayTypes[0] != null)
{
Object[] objs = new Object[parms.Length];
int lastPos = parms.Length - 1;
Array.Copy(args, 0, objs, 0, lastPos);
objs[lastPos] = Array.CreateInstance(paramArrayTypes[0], 1);
((Array)objs[lastPos]).SetValue(args[lastPos], 0);
args = objs;
}
}
else if (parms.Length > args.Length)
{
Object[] objs = new Object[parms.Length];
for (i=0;i<args.Length;i++)
objs[i] = args[i];
for (;i<parms.Length - 1;i++)
objs[i] = parms[i].DefaultValue;
if (paramArrayTypes[0] != null)
objs[i] = Array.CreateInstance(paramArrayTypes[0], 0); // create an empty array for the
else
objs[i] = parms[i].DefaultValue;
args = objs;
}
else
{
if ((canidates[0].CallingConvention & CallingConventions.VarArgs) == 0)
{
Object[] objs = new Object[parms.Length];
int paramArrayPos = parms.Length - 1;
Array.Copy(args, 0, objs, 0, paramArrayPos);
objs[paramArrayPos] = Array.CreateInstance(paramArrayTypes[0], args.Length - paramArrayPos);
Array.Copy(args, paramArrayPos, (System.Array)objs[paramArrayPos], 0, args.Length - paramArrayPos);
args = objs;
}
}
#endregion
return canidates[0];
}
int currentMin = 0;
bool ambig = false;
for (i = 1; i < CurIdx; i++)
{
#region Walk all of the methods looking the most specific method to invoke
int newMin = FindMostSpecificMethod(canidates[currentMin], paramOrder[currentMin], paramArrayTypes[currentMin],
canidates[i], paramOrder[i], paramArrayTypes[i], argTypes, args);
if (newMin == 0)
{
ambig = true;
}
else if (newMin == 2)
{
currentMin = i;
ambig = false;
}
#endregion
}
if (ambig)
throw new AmbiguousMatchException(Environment.GetResourceString("RFLCT.Ambiguous"));
// Reorder (if needed)
if (names != null) {
state = new BinderState((int[])paramOrder[currentMin].Clone(), args.Length, paramArrayTypes[currentMin] != null);
ReorderParams(paramOrder[currentMin], args);
}
// If the parameters and the args are not the same length or there is a paramArray
// then we need to create a argument array.
ParameterInfo[] parameters = canidates[currentMin].GetParametersNoCopy();
if (parameters.Length == args.Length)
{
if (paramArrayTypes[currentMin] != null)
{
Object[] objs = new Object[parameters.Length];
int lastPos = parameters.Length - 1;
Array.Copy(args, 0, objs, 0, lastPos);
objs[lastPos] = Array.CreateInstance(paramArrayTypes[currentMin], 1);
((Array)objs[lastPos]).SetValue(args[lastPos], 0);
args = objs;
}
}
else if (parameters.Length > args.Length)
{
Object[] objs = new Object[parameters.Length];
for (i=0;i<args.Length;i++)
objs[i] = args[i];
for (;i<parameters.Length - 1;i++)
objs[i] = parameters[i].DefaultValue;
if (paramArrayTypes[currentMin] != null)
{
objs[i] = Array.CreateInstance(paramArrayTypes[currentMin], 0);
}
else
{
objs[i] = parameters[i].DefaultValue;
}
args = objs;
}
else
{
if ((canidates[currentMin].CallingConvention & CallingConventions.VarArgs) == 0)
{
Object[] objs = new Object[parameters.Length];
int paramArrayPos = parameters.Length - 1;
Array.Copy(args, 0, objs, 0, paramArrayPos);
objs[i] = Array.CreateInstance(paramArrayTypes[currentMin], args.Length - paramArrayPos);
Array.Copy(args, paramArrayPos, (System.Array)objs[i], 0, args.Length - paramArrayPos);
args = objs;
}
}
return canidates[currentMin];
}