/// <summary> /// Normally, making a stub method on a baseclass requires two overrides with the same name. /// An override method, and a new delegate field with the same name. /// But for generic methods, a delegate field with the same name won't work. /// Instead, we have a generic delegate, a dictionary for overrides, a caller for the base method, an "ImplementMember" method, and an override for the method. /// Since all of these have different names, we can put all the overrides in a single class. /// So don't put anything in the helper intermediate class. /// </summary> private void AppendGenericMethod(MethodInfo info, MemberMetadata metadata) { var typesExtension = StubBuilder.SanitizeMethodName(metadata.ParameterTypes); var typeofList = info.GetGenericArguments().Select(type => $"typeof({type.Name})").Aggregate((a, b) => $"{a}, {b}"); var createKey = $"var key = new Type[] {{ {typeofList} }};"; var delegateName = $"{metadata.Name}Delegate_{typesExtension}{metadata.GenericParameters}"; var dictionary = $"{metadata.Name}Delegates_{typesExtension}"; var methodName = $"{metadata.Name}{metadata.GenericParameters}"; stubWriter.Write($"public delegate {metadata.ReturnType} {delegateName}({metadata.ParameterTypesAndNames}){metadata.GenericParameterConstraints};"); stubWriter.Write($"private readonly Dictionary<Type[], object> {dictionary} = new Dictionary<Type[], object>(new EnumerableEqualityComparer<Type>());"); stubWriter.Write($"public void Implement{methodName}({delegateName} implementation){metadata.GenericParameterConstraints}"); using (stubWriter.Scope) { stubWriter.Write(createKey); stubWriter.Write($"{dictionary}[key] = implementation;"); } if (!info.IsAbstract) { stubWriter.Write($"public {metadata.ReturnType} Base{methodName}({metadata.ParameterTypesAndNames}){metadata.GenericParameterConstraints}"); using (stubWriter.Scope) { stubWriter.Write($"{metadata.ReturnClause}base.{methodName}({metadata.ParameterNames});"); } } stubWriter.Write($"{metadata.Access} override {metadata.ReturnType} {methodName}({metadata.ParameterTypesAndNames})"); using (stubWriter.Scope) { stubWriter.AssignDefaultValuesToOutParameters(info.DeclaringType.Namespace, info.GetParameters()); stubWriter.Write(createKey); stubWriter.Write("object implementation;"); stubWriter.Write($"if ({dictionary}.TryGetValue(key, out implementation))"); using (stubWriter.Scope) { stubWriter.Write($"{metadata.ReturnClause}(({delegateName})implementation).Invoke({metadata.ParameterNames});"); } stubWriter.Write("else"); using (stubWriter.Scope) { if (!info.IsAbstract) { stubWriter.Write($"{metadata.ReturnClause}Base{methodName}({metadata.ParameterNames});"); } else if (metadata.ReturnType != "void") { stubWriter.Write($"return default({metadata.ReturnType});"); } } } }
public void AppendMethod(MethodInfo info, MemberMetadata metadata) { if (info.IsStatic || info.IsPrivate || info.IsAssembly || info.IsFamilyAndAssembly) { return; } if (!info.IsVirtual && !info.IsAbstract) { if (metadata.Access == "protected") { // the member is protected. Make a public version. stubWriter.Write($"public new {metadata.ReturnType} {metadata.Name}{metadata.GenericParameters}({metadata.ParameterTypesAndNames}){metadata.GenericParameterConstraints}"); using (stubWriter.Scope) { stubWriter.Write($"{metadata.ReturnClause}base.{metadata.Name}({metadata.ParameterNames});"); } } return; } if (info.IsGenericMethodDefinition) { AppendGenericMethod(info, metadata); return; } var delegateName = GetDelegateName(metadata.ReturnType, metadata.ParameterTypes); var typesExtension = StubBuilder.SanitizeMethodName(metadata.ParameterTypes); var methodsWithMatchingNameButNotSignature = implementedMethods.Where(name => name.Split('(')[0] == metadata.Name && name != $"{metadata.Name}({metadata.ParameterTypes})"); string localImplementationName = methodsWithMatchingNameButNotSignature.Any() ? $"{metadata.Name}_{typesExtension}" : metadata.Name; if (info.GetParameters().Any(p => p.ParameterType.IsByRef)) { delegateName = $"{metadata.Name}Delegate_{typesExtension}"; stubWriter.Write($"public delegate {metadata.ReturnType} {delegateName}({metadata.ParameterTypesAndNames});" + Environment.NewLine); } stubWriter.Write($"public new {delegateName} {localImplementationName};"); WriteHelperBaseMethod(info, metadata); WriteHelperMethod(info, metadata, stubTypeName, localImplementationName); implementedMethods.Add($"{metadata.Name}({metadata.ParameterTypes})"); }
private void AppendToConstructorFromMethod(MethodInfo info, MemberMetadata metadata, CSharpSourceWriter deferWriter) { if (info.IsSpecialName || info.IsStatic || info.IsPrivate || !info.IsVirtual || info.IsAbstract || info.IsAssembly || info.IsFamilyAndAssembly || info.IsGenericMethod) { return; } if (info.IsVirtual && info.Name == "Finalize") { return; // Finalize is special in C#. Use a destructor instead. } var typesExtension = StubBuilder.SanitizeMethodName(metadata.ParameterTypes); var methodsWithMatchingNameButNotSignature = implementedMethods.Where(name => name.Split('(')[0] == metadata.Name && name != $"{metadata.Name}({metadata.ParameterTypes})"); string localImplementationName = methodsWithMatchingNameButNotSignature.Any() ? $"{metadata.Name}_{typesExtension}" : metadata.Name; stubWriter.Write($"if ({localImplementationName} == null) {localImplementationName} = Base{metadata.Name};"); deferWriter.Write($"stub.{localImplementationName} = stub.Base{metadata.Name};"); implementedMethods.Add($"{metadata.Name}({metadata.ParameterTypes})"); }