///<example> ///public delegate void MethodWithGenericInputDelegate_T<T>(T input); ///private readonly Dictionary<Type[], object> MethodWithGenericInputDelegates_T = new Dictionary<Type[], object>(); ///public void ImplementMethodWithGenericInput<T>(MethodWithGenericInputDelegate_T<T> implementation) ///{ /// var key = new Type[] { typeof(T) }; /// MethodWithGenericInputDelegates[key] = implementation; ///} ///public void MethodWithGenericInput<T>(T input) ///{ /// var key = new Type[] { typeof(T) }; /// object implementation; /// if (MethodWithGenericInputDelegates.TryGetValue(key, out implementation)) /// { /// ((MethodWithGenericInputDelegate<T>)implementation).Invoke(input); /// } ///} ///</example> private void AppendGenericMethod(MethodInfo info, MemberMetadata method) { var typesExtension = SanitizeMethodName(method.ParameterTypes); var typeofList = info.GetGenericArguments().Select(type => $"typeof({type.Name})").Aggregate((a, b) => $"{a}, {b}"); var createKey = $"var key = new Type[] {{ {typeofList} }};"; var delegateName = $"{method.Name}Delegate_{typesExtension}{method.GenericParameters}"; var dictionary = $"{method.Name}Delegates_{typesExtension}"; var methodName = $"{method.Name}{method.GenericParameters}"; writer.Write($"public delegate {method.ReturnType} {delegateName}({method.ParameterTypesAndNames}){method.GenericParameterConstraints};"); writer.Write($"private readonly Dictionary<Type[], object> {dictionary} = new Dictionary<Type[], object>(new EnumerableEqualityComparer<Type>());"); writer.Write($"public void Implement{methodName}({delegateName} implementation){method.GenericParameterConstraints}"); using (writer.Scope) { writer.Write(createKey); writer.Write($"{dictionary}[key] = implementation;"); } writer.Write($"public {method.ReturnType} {methodName}({method.ParameterTypesAndNames}){method.GenericParameterConstraints}"); using (writer.Scope) { writer.AssignDefaultValuesToOutParameters(info.DeclaringType.Namespace, info.GetParameters()); writer.Write(createKey); writer.Write("object implementation;"); writer.Write($"if ({dictionary}.TryGetValue(key, out implementation))"); using (writer.Scope) { writer.Write($"{method.ReturnClause}(({delegateName})implementation).Invoke({method.ParameterNames});"); } if (method.ReturnType != "void") { writer.Write("else"); using (writer.Scope) { writer.Write($"return default({method.ReturnType});"); } } } writer.Write(string.Empty); }
// <example> // public void DoThing(int arg) // { // for (int i = 0; i < base.Count; i++) // { // base[i].DoThing(arg); // } // } // </example> /// <remarks> /// Composite methods with return types are a bit strange. /// If all the methods agree on what to return, then return that. /// If any are different, then just return default. /// In the case of nullables and bools, this default seems appropriate. /// But it can be strange for numeric types. /// /// For methods that return void, a composite simply forwards the method call down to each thing that it contains. /// </remarks> public void AppendMethod(MethodInfo info, MemberMetadata method) { // Use an explicit implementation only if the signature has already been used // example: IEnumerable<T>, which extends IEnumerable if (!implementedMethods.Any(name => name == $"{method.Name}({method.ParameterTypes})")) { writer.Write($"public virtual {method.ReturnType} {method.Name}{method.GenericParameters}({method.ParameterTypesAndNames}){method.GenericParameterConstraints}"); } else { writer.Write($"{method.ReturnType} {method.DeclaringType}.{method.Name}{method.GenericParameters}({method.ParameterTypesAndNames}){method.GenericParameterConstraints}"); } using (writer.Scope) { writer.AssignDefaultValuesToOutParameters(info.DeclaringType.Namespace, info.GetParameters()); if (method.ReturnType == "void") { writer.Write("for (int i = 0; i < base.Count; i++)"); using (writer.Scope) { writer.Write($"base[i].{method.Name}{method.GenericParameters}({method.ParameterNames});"); } } else { writer.Write($"var results = new System.Collections.Generic.List<{method.ReturnType}>();"); writer.Write("for (int i = 0; i < base.Count; i++)"); using (writer.Scope) { writer.Write($"results.Add(base[i].{method.Name}{method.GenericParameters}({method.ParameterNames}));"); } writer.Write("if (results.Count > 0 && results.All(result => result.Equals(results[0])))"); using (writer.Scope) { writer.Write("return results[0];"); } writer.Write($"return default({method.ReturnType});"); } } writer.Write(string.Empty); implementedMethods.Add($"{method.Name}({method.ParameterTypes})"); }