private static List <MethodInfo> getCandidateMethods(Type targetType, string action) { Dictionary <string, List <MethodInfo> > methodDispatchTable = getMethodDispatchTable(targetType); if (!methodDispatchTable.ContainsKey(action)) { List <MethodInfo> methods = new List <MethodInfo>(); List <Type> hierarchy = new List <Type>(); // not completely generic Type ht = targetType; while (ht != typeof(object)) { hierarchy.Add(ht); ht = ht.BaseType; } foreach (MethodInfo mi in getMethods(targetType)) { if (mi.ReturnType != typeof(void) || mi.Name != action) { // We only allow dispatching to public void methods continue; } bool replaced = false; // we do this to handle the case of a child method hiding a method in the parent // in which case both methods will show up. This happens if virtual, override or new // are not used ParameterInfo[] sourceParams = mi.GetParameters(); for (int j = 0; j < methods.Count && !replaced; j++) { bool signatureMatch = true; ParameterInfo[] targetParams = methods[j].GetParameters(); int minCommon = Math.Min(sourceParams.Length, targetParams.Length); for (int k = 0; k < minCommon; k++) { if (sourceParams[k].ParameterType != targetParams[k].ParameterType) { signatureMatch = false; } } if (sourceParams.Length > targetParams.Length && !sourceParams[minCommon].HasDefaultValue) { signatureMatch = false; } else if (targetParams.Length > sourceParams.Length && !targetParams[minCommon].HasDefaultValue) { signatureMatch = false; } // if the method is more specific and the parameters match // we will dispatch to this method instead of the base type if (signatureMatch) { // this happens if one method has a trailing optional value and all // other parameter types match if (targetParams.Length != sourceParams.Length) { throw new AmbiguousActionException("Signature match found in the same class"); } replaced = true; if (hierarchy.IndexOf(mi.DeclaringType) < hierarchy.IndexOf(methods[j].DeclaringType)) { methods[j] = mi; } } } if (!replaced) { // we sort the list of methods so that we evaluate // methods with fewer and possible no params first // and then match methods with greater params methods.Add(mi); MethodParamComparer mc = new MethodParamComparer(); methods.Sort(mc); } } // must perform assignment here, since an exception could be thrown during // the creation of the list. if the list were assigned directly to the allMethodDispatchTable // its possible that it would be only partially populated methodDispatchTable[action] = methods; } return(methodDispatchTable[action]); }
private static MethodInfo getDispatchMethod(Type targetType, dynamic serverCommand) { if (!methodDispatchTable.ContainsKey(targetType)) { System.Reflection.MethodInfo[] allMethods = targetType.GetMethods(BindingFlags.Public | BindingFlags.Instance); List <Type> hierarchy = new List <Type>(); // not completely generic Type ht = targetType; while (ht != typeof(object)) { hierarchy.Add(ht); ht = ht.BaseType; } Dictionary <string, List <MethodInfo> > methodDispatch = new Dictionary <string, List <MethodInfo> >(); foreach (MethodInfo mi in allMethods) { if (mi.ReturnType != typeof(void)) { // We only allow dispatching to public void methods continue; } if (methodDispatch.ContainsKey(mi.Name)) { List <MethodInfo> methods = methodDispatch[mi.Name]; bool replaced = false; // we do this to handle the case of a child method hiding a method in the parent // in which case both methods will show up. This happens if virtual, override or new // are not used ParameterInfo[] sourceParams = mi.GetParameters(); for (int j = 0; j < methods.Count && !replaced; j++) { bool signatureMatch = true; ParameterInfo[] targetParams = methods[j].GetParameters(); if (targetParams.Length == sourceParams.Length) { for (int k = 0; k < sourceParams.Length; k++) { if (sourceParams[k].ParameterType != targetParams[k].ParameterType) { signatureMatch = false; } } } else { signatureMatch = false; } // if the method is more specific and the parameters match // we will dispatch to this method instead of the base type if (signatureMatch) { replaced = true; if (hierarchy.IndexOf(mi.DeclaringType) < hierarchy.IndexOf(methods[j].DeclaringType)) { methods[j] = mi; } } } if (!replaced) { // we sort the list of methods so that we evaluate // methods with fewer and possible no params first // and then match methods with greater params methods.Add(mi); MethodParamComparer mc = new MethodParamComparer(); methods.Sort(mc); } } else { methodDispatch[mi.Name] = new List <MethodInfo>(); methodDispatch[mi.Name].Add(mi); } } methodDispatchTable[targetType] = methodDispatch; } List <MethodInfo> actionMethods = null; methodDispatchTable[targetType].TryGetValue(serverCommand.action.ToString(), out actionMethods); MethodInfo matchedMethod = null; int bestMatchCount = -1; // we do this so that if (actionMethods != null) { // This is where the the actual matching occurs. The matching is done strictly based on // variable names. In the future, this could be modified to include type information from // the inbound JSON object by mapping JSON types to csharp primitive types // (i.e. number -> [short, float, int], bool -> bool, string -> string, dict -> object, list -> list) foreach (var method in actionMethods) { int matchCount = 0; ParameterInfo[] mParams = method.GetParameters(); // default to ServerAction method // this is also necessary, to allow Initialize to be // called in the AgentManager and an Agent, since we // pass a ServerAction through if (matchedMethod == null && mParams.Length == 1 && mParams[0].ParameterType == typeof(ServerAction)) { matchedMethod = method; } else { HashSet <string> actionParams = new HashSet <string>(); foreach (var p in serverCommand.Properties()) { actionParams.Add(p.Name); } foreach (var p in method.GetParameters()) { if (actionParams.Contains(p.Name)) { matchCount++; } } } // preference is given to the method that matches all parameters for a method // even if another method has the same matchCount (but has more parameters) if (matchCount > bestMatchCount) { bestMatchCount = matchCount; matchedMethod = method; } } } return(matchedMethod); }