/// <summary> /// Adds additional <see cref="ICriteria{T}"/> to the fuzzy search list. /// </summary> /// <param name="methods">The list of methods to rank.</param> /// <param name="finderContext">The <see cref="IMethodFinderContext"/> that describes the target method.</param> protected override void Rank(IList <IFuzzyItem <TMethod> > methods, IMethodFinderContext finderContext) { var additionalArguments = finderContext.Arguments ?? new object[0]; var argumentTypes = (from argument in additionalArguments let argumentType = argument == null ? typeof(object) : argument.GetType() select argumentType).ToList(); var argumentCount = argumentTypes.Count; foreach (var fuzzyItem in methods) { if (fuzzyItem.Confidence < 0) { continue; } // Check the constructor for any // parameter types that might not exist // in the container and eliminate the // constructor as a candidate match if // that parameter type cannot be found var constructor = fuzzyItem.Item; var parameters = constructor.GetParameters(); var parameterCount = parameters.Length; var maxRelativeIndex = parameterCount - argumentCount; CheckParameters(fuzzyItem, Container, maxRelativeIndex); } }
/// <summary> /// Uses the <paramref name="container"/> to determine which member to use from /// the <paramref name="concreteType">concrete type</paramref>. /// </summary> /// <param name="concreteType">The target type.</param> /// <param name="container">The container that contains the member values that will be used to invoke the members.</param> /// <param name="finderContext">The <see cref="IMethodFinderContext"/> that describes the target method.</param> /// <returns>A member instance if a match is found; otherwise, it will return <c>null</c>.</returns> public TMember ResolveFrom(Type concreteType, IServiceContainer container, IMethodFinderContext finderContext) { IEnumerable <TMember> constructors = GetMembers(concreteType); if (constructors == null) { return(null); } IMethodFinder <TMember> resolver = GetMethodFinder(container); TMember bestMatch = resolver.GetBestMatch(constructors, finderContext); // If all else fails, find the // default constructor and use it as the // best match by default if (bestMatch == null) { TMember defaultResult = GetDefaultResult(concreteType); bestMatch = defaultResult; } Debug.Assert(bestMatch != null); return(bestMatch); }
/// <summary> /// Determines which method best matches the /// services currently in the target container. /// </summary> /// <param name="items">The list of methods to search.</param> /// <param name="finderContext">The <see cref="IMethodFinderContext"/> that describes the target method.</param> /// <returns>Returns the method with the most resolvable parameters from the target <see cref="IServiceContainer"/> instance.</returns> public T GetBestMatch(IEnumerable <T> items, IMethodFinderContext finderContext) { T bestMatch = null; IList <IFuzzyItem <T> > fuzzyList = items.AsFuzzyList(); // Return the first item // if there is no other alternative if (fuzzyList.Count == 1) { return(fuzzyList[0].Item); } IEnumerable <object> additionalArguments = finderContext.Arguments; List <Type> additionalArgumentTypes = (from argument in additionalArguments let argumentType = argument == null ? typeof(object) : argument.GetType() select argumentType).ToList(); Rank(fuzzyList, finderContext); // Match the generic parameter types int genericParameterCount = finderContext.TypeArguments != null?finderContext.TypeArguments.Count() : 0; Func <T, bool> matchGenericArgumentCount = method => { Type[] genericArguments = method.IsGenericMethod ? method.GetGenericArguments () : new Type[0]; int currentParameterCount = genericArguments.Count(); return(genericParameterCount == currentParameterCount); }; fuzzyList.AddCriteria(matchGenericArgumentCount, CriteriaType.Critical); IEnumerable <IFuzzyItem <T> > candidates = fuzzyList.Where(fuzzy => fuzzy.Confidence > 0); bestMatch = SelectBestMatch(candidates); // If all else fails, find the method // that matches only the additional arguments if (bestMatch != null) { return(bestMatch); } return(GetNextBestMatch(fuzzyList, additionalArgumentTypes, bestMatch)); }
/// <summary> /// Adds additional <see cref="ICriteria{T}"/> to the fuzzy search list. /// </summary> /// <param name="methods">The list of methods to rank.</param> /// <param name="finderContext">The <see cref="IMethodFinderContext"/> that describes the target method.</param> protected virtual void Rank(IList <IFuzzyItem <T> > methods, IMethodFinderContext finderContext) { }
public Option<TMethod> GetBestMatch(IEnumerable<TMethod> methods, IMethodFinderContext finderContext) { var methodName = finderContext.MethodName; var candidateMethods = (methods ?? Enumerable.Empty<TMethod>()); // Match the method name if (methodName.HasValue && !string.IsNullOrEmpty(methodName.ValueOrDefault())) candidateMethods = candidateMethods.Where(m => _methodFinderStrategy.GetMethodName(m) == methodName.ValueOrFailure()); // Match the argument count var argumentTypes = finderContext.ArgumentTypes.ToArray(); var argumentCount = argumentTypes.Count(); candidateMethods = candidateMethods.Where(m => _methodFinderStrategy.GetParameterTypes(m).Count() == argumentCount); // Find a compatible method signature bool HasCompatibleParameters(TMethod method, int position, IReadOnlyList<Option<Type>> currentArgumentTypes) { var parameters = _methodFinderStrategy.GetParameterTypes(method).ToArray(); if (currentArgumentTypes.Count != parameters.Length) return false; var parameterType = parameters[position]; var argumentType = currentArgumentTypes[position]; if (!argumentType.HasValue) return false; var hasCompatibleParameterType = parameterType.IsAssignableFrom(argumentType.ValueOrFailure()); return hasCompatibleParameterType; } // Exact parameter type matches will outweigh compatible method overloads bool HasExactParameterTypes(TMethod method, int position, IReadOnlyList<Option<Type>> currentArguments) { var parameters = _methodFinderStrategy.GetParameterTypes(method).ToArray(); if (currentArguments.Count != parameters.Length) return false; var parameterType = parameters[position]; var argumentType = currentArguments[position]; return argumentType.HasValue && parameterType == argumentType.ValueOrFailure(); } bool HasCompatibleReturnType(TMethod method, Type expectedReturnType) { var returnType = _methodFinderStrategy.GetReturnType(method); return returnType.HasValue && expectedReturnType.IsAssignableFrom(returnType.ValueOrFailure()); } var fuzzyList = candidateMethods.AsFuzzyList(); // Override the search results if there is only one match // and that match has compatible parameters var hasRequestedCompatibleMethodType = finderContext.ReturnType.HasValue; if (fuzzyList.Count == 1) { var nextBestMatch = fuzzyList[0]; var hasCompatibleParameters = true; for (var i = 0; i < argumentTypes.Length; i++) { hasCompatibleParameters &= HasCompatibleParameters(nextBestMatch.Item, i, argumentTypes); } // Match the return type if the caller requests a method return type match if (hasRequestedCompatibleMethodType && hasCompatibleParameters) return HasCompatibleReturnType(nextBestMatch.Item, finderContext.ReturnType.ValueOrFailure()) ? Option.Some(nextBestMatch.Item) : Option.None<TMethod>(); if (hasCompatibleParameters) return Option.Some(nextBestMatch.Item); } // Otherwise, fall back to a weighted search against the other remaining methods for (var i = 0; i < argumentTypes.Length; i++) { var currentIndex = i; fuzzyList.AddCriteria(method => HasCompatibleParameters(method, currentIndex, argumentTypes), CriteriaType.Critical); fuzzyList.AddCriteria(method => HasExactParameterTypes(method, currentIndex, argumentTypes)); } // Match the method return type if (hasRequestedCompatibleMethodType) fuzzyList.AddCriteria( method => HasCompatibleReturnType(method, finderContext.ReturnType.ValueOrFailure()), CriteriaType.Critical); if (argumentTypes.Length == 0) fuzzyList.AddCriteria(method => !_methodFinderStrategy.GetParameterTypes(method).Any()); var bestMatch = fuzzyList.BestMatch(_finderTolerance); return bestMatch == null ? Option.None<TMethod>() : Option.Some(bestMatch?.Item); }
public static bool HasMatchingMethods <TMethod>(this MethodBaseFinder <TMethod> finder, IEnumerable <TMethod> methods, IMethodFinderContext context) where TMethod : MethodBase { return(finder.GetBestMatch(methods, context).HasValue); }