private void AppendConstructor(Type type, ConstructorInfo info, MemberMetadata constructorMetadata) { if (info.IsPrivate || info.IsStatic || info.IsFamilyAndAssembly || info.IsAssembly) { return; } var typeName = type.CreateCsName(type.Namespace); var(basename, genericInfo) = typeName.ExtractImplementationNameParts("<"); var intermediateName = $"IntermediateStub{basename}_DoNotUse"; var stubName = $"Stub{basename}"; // for every constructor, make both a constructor and a static "DeferConstruction" method var deferWriter = new CSharpSourceWriter(stubWriter.Indentation); stubWriter.Write($"public {stubName}({constructorMetadata.ParameterTypesAndNames}) : base({constructorMetadata.ParameterNames})"); var outParam = $"out {stubTypeName} uninitializedStub"; var separator = constructorMetadata.ParameterTypesAndNames.Length > 0 ? ", " : string.Empty; deferWriter.Write($"public static IDisposable DeferConstruction({constructorMetadata.ParameterTypesAndNames}{separator}{outParam})"); using (stubWriter.Scope) { using (deferWriter.Scope) { deferWriter.Write($"{stubTypeName} stub;"); deferWriter.Write($"var disposable = ConstructionCompletion.CreateObjectWithDeferredConstruction<{stubTypeName}>(out stub{separator}{constructorMetadata.ParameterNames});"); foreach (var member in Program.FindAllMembers(type)) { var metadata = new MemberMetadata(member, type.Namespace); switch (member.MemberType) { case MemberTypes.Method: AppendToConstructorFromMethod((MethodInfo)member, metadata, deferWriter); break; case MemberTypes.Event: AppendToConstructorFromEvent((EventInfo)member, metadata, deferWriter); break; case MemberTypes.Property: AppendToConstructorFromProperty((PropertyInfo)member, metadata, deferWriter); break; default: // the only other options are Field, Type, NestedType, and Constructor // none of those can be virtual/abstract, so we don't need to put anything in the constructor for them. break; } } deferWriter.Write($"uninitializedStub = stub;"); deferWriter.Write($"return disposable;"); } } stubWriter.Write(deferWriter.ToString()); intermediateWriter.Write($"protected {intermediateName}({constructorMetadata.ParameterTypesAndNames}) : base({constructorMetadata.ParameterNames}) {{ }}"); }
/// <summary> /// Creates a Builder of the given generic type to implement the given interface. /// Output is placed in the given fileName. /// </summary> private static void GenerateImplementation <TPatternBuilder>(Type type) where TPatternBuilder : IPatternBuilder { var writer = new CSharpSourceWriter(numberOfSpacesToIndent: 4); var builder = (TPatternBuilder)Activator.CreateInstance(typeof(TPatternBuilder), writer); var fileName = builder.GetDesiredOutputFileName(type); Console.WriteLine($"Generating {fileName} ..."); writer.Write($"// this file was created by AutoImplement"); writer.Write($"namespace {type.Namespace}"); using (writer.Scope) { writer.Write($"public class {builder.ClassDeclaration(type)}"); using (writer.Scope) { builder.AppendExtraMembers(type); var allMembers = FindAllMembers(type); foreach (var member in allMembers) { var metadata = new MemberMetadata(member, type.Namespace); switch (member.MemberType) { case MemberTypes.Method: ImplementMethod(member, metadata, builder); break; case MemberTypes.Event: ImplementEvent(member, metadata, builder); break; case MemberTypes.Property: ImplementProperty(member, metadata, builder); break; default: // the only other options are Field, Type, NestedType, and Constructor // for classes, any of these are possible, and all can be ignored. break; } } } builder.BuildCompleted(); } File.WriteAllText(fileName, writer.ToString()); }