Exemplo n.º 1
0
        private static void ImplementMethod(MemberInfo info, MemberMetadata metadata, IPatternBuilder builder)
        {
            var methodInfo = (MethodInfo)info;

            if (methodInfo.IsSpecialName)
            {
                return;
            }
            if (metadata.Name == "Finalize" && methodInfo.IsVirtual)
            {
                return;                                                   // Finalize is special: do not override it. Use a destructor instead.
            }
            builder.AppendMethod(methodInfo, metadata);
        }
Exemplo n.º 2
0
        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}) {{ }}");
        }
Exemplo n.º 3
0
        public string ClassDeclaration(Type type)
        {
            var    typeName = type.CreateCsName(type.Namespace);
            string basename;

            (basename, genericInfo) = typeName.ExtractImplementationNameParts("<");
            constraints             = MemberMetadata.GetGenericParameterConstraints(type.GetGenericArguments(), type.Namespace);

            intermediateWriter.Write($"public class IntermediateStub{basename}_DoNotUse{genericInfo} : {typeName}{constraints}");
            Debug.Assert(intermediateClassScope == null);
            intermediateClassScope = intermediateWriter.Scope;
            stubTypeName           = $"Stub{basename}{genericInfo}";

            return($"{stubTypeName} : IntermediateStub{basename}_DoNotUse{genericInfo}{constraints}");
        }
Exemplo n.º 4
0
 /// <example>
 // public EventImplementation<EventArgs> ValueChanged = new EventImplementation<EventHandler>();
 //
 // event EventHandler INotifyValueChanged.ValueChanged
 // {
 //    add
 //    {
 //       ValueChanged.add(new EventHandler<EventArgs>(value));
 //    }
 //    remove
 //    {
 //       ValueChanged.remove(new EventHandler<EventArgs>(value));
 //    }
 // }
 /// </example>
 /// <remarks>
 /// Events are replaces with an EventImplementation field.
 /// Explicit interface implementations then call that EventImplementation.
 ///
 /// EventImplementation exposes add, remove, handlers, and +/- operators along with an Invoke method.
 /// This allows you to assign custom add/remove handlers to the Stub, or make decision based on the individual added handlers,
 /// or use +=, -=, and .Invoke as if the EventImplementation were actually an event.
 ///
 // Note that the explicit implementation always casts added/removed delegates to EventHandler<T>.
 // This is to avoid having to deal with .net's 2 types of EventHandlers separately.
 // Example: RoutedEventHandler vs EventHandler<RoutedEventArgs>.
 /// </remarks>
 public void AppendEvent(EventInfo info, MemberMetadata eventData)
 {
     writer.Write($"public EventImplementation<{eventData.HandlerArgsType}> {info.Name} = new EventImplementation<{eventData.HandlerArgsType}>();");
     writer.Write(string.Empty);
     writer.Write($"event {eventData.HandlerType} {eventData.DeclaringType}.{info.Name}");
     using (writer.Scope) {
         writer.Write("add");
         using (writer.Scope) {
             writer.Write($"{info.Name}.add(new EventHandler<{eventData.HandlerArgsType}>(value));");
         }
         writer.Write("remove");
         using (writer.Scope) {
             writer.Write($"{info.Name}.remove(new EventHandler<{eventData.HandlerArgsType}>(value));");
         }
     }
 }
Exemplo n.º 5
0
        /// <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});");
                    }
                }
            }
        }
Exemplo n.º 6
0
        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})");
        }
Exemplo n.º 7
0
 private void WriteHelperMethod(MethodInfo info, MemberMetadata metadata, string stubTypeName, string localImplementationName)
 {
     intermediateWriter.Write($"{metadata.Access} override {metadata.ReturnType} {metadata.Name}({metadata.ParameterTypesAndNames}){metadata.GenericParameterConstraints}");
     using (intermediateWriter.Scope) {
         var call = $"(({stubTypeName})this).{localImplementationName}";
         intermediateWriter.AssignDefaultValuesToOutParameters(info.DeclaringType.Namespace, info.GetParameters());
         intermediateWriter.Write($"if ({call} != null)");
         using (intermediateWriter.Scope) {
             intermediateWriter.Write($"{metadata.ReturnClause}{call}({metadata.ParameterNames});");
         }
         if (metadata.ReturnType != "void")
         {
             intermediateWriter.Write("else");
             using (intermediateWriter.Scope) {
                 intermediateWriter.Write($"return default({metadata.ReturnType});");
             }
         }
     }
 }
Exemplo n.º 8
0
        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})");
        }
Exemplo n.º 9
0
 private void AppendPropertyCommon(PropertyInfo info, MemberMetadata property, string listItem)
 {
     using (writer.Scope) {
         if (info.CanRead)
         {
             writer.Write("get");
             using (writer.Scope) {
                 writer.Write($"var results = this.Select<{property.DeclaringType}, {property.ReturnType}>(listItem => {listItem}).ToList();");
                 writer.Write($"return results.Count > 0 && results.All(result => result.Equals(results[0])) ? results[0] : default({property.ReturnType});");
             }
         }
         if (info.CanWrite)
         {
             writer.Write("set");
             using (writer.Scope) {
                 writer.Write($"this.ForEach(listItem => {listItem} = value);");
             }
         }
     }
 }
Exemplo n.º 10
0
        // <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})");
        }
Exemplo n.º 11
0
        /// <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());
        }
Exemplo n.º 12
0
        /// <remarks>
        /// Since Item properties in .net have parameters, the Item property has to be handled specially.
        /// Instead of using a PropertyImplementation object, two separate delegates are exposed, named get_Item and set_Item.
        /// The get and set of the Item property forward to these two public fields.
        /// If no implementation is provided, get_Item will just return default.
        /// </remarks>
        public void AppendItemProperty(PropertyInfo info, MemberMetadata property)
        {
            // define the backing get/set_Item methods if this property hasn't been implemented yet
            if (!implementedProperties.Contains(property.Name))
            {
                if (info.CanRead)
                {
                    writer.Write($"public Func<{property.ParameterTypes}, {property.ReturnType}> get_Item = ({property.ParameterNames}) => default({property.ReturnType});" + Environment.NewLine);
                }

                if (info.CanWrite)
                {
                    writer.Write($"public Action<{property.ParameterTypes}, {property.ReturnType}> set_Item = ({property.ParameterNames}, value) => {{}};" + Environment.NewLine);
                }

                implementedProperties.Add(property.Name);
            }

            // define the explicit interface implementation
            // this may run multiple times if the same property is defined on multiple interfaces (example, IReadOnlyList and IList)
            writer.Write($"{property.ReturnType} {property.DeclaringType}.this[{property.ParameterTypesAndNames}]");
            using (writer.Scope) {
                if (info.CanRead)
                {
                    writer.Write("get");
                    using (writer.Scope) {
                        writer.Write($"return get_Item({property.ParameterNames});");
                    }
                }
                if (info.CanWrite)
                {
                    writer.Write("set");
                    using (writer.Scope) {
                        writer.Write($"set_Item({property.ParameterNames}, value);");
                    }
                }
            }
        }
Exemplo n.º 13
0
        public void AppendExtraMembers(Type type)
        {
            // add in constructors
            var constructors = type.GetConstructors(BindingFlags.Instance | BindingFlags.NonPublic)
                               .Concat(type.GetConstructors(BindingFlags.Instance | BindingFlags.Public));

            foreach (var constructor in constructors)
            {
                var metadata = new MemberMetadata(constructor, type.Namespace);
                AppendConstructor(type, constructor, metadata);
            }

            implementedMethods.Clear();

            // add in fields
            var protectedFields = type.GetFields(BindingFlags.Instance | BindingFlags.NonPublic).Where(field => field.IsFamily || field.IsFamilyOrAssembly);

            foreach (var field in protectedFields)
            {
                var metadata = new MemberMetadata(field, type.Namespace);
                AppendField(type, field, metadata);
            }
        }
Exemplo n.º 14
0
        private static void ImplementEvent(MemberInfo info, MemberMetadata metadata, IPatternBuilder builder)
        {
            var eventInfo = (EventInfo)info;

            builder.AppendEvent(eventInfo, metadata);
        }
Exemplo n.º 15
0
        private void ImplementInterfaceMethod(MethodInfo info, string localImplementationName, MemberMetadata method)
        {
            var call = $"this.{localImplementationName}";

            writer.Write($"{method.ReturnType} {method.DeclaringType}.{method.Name}({method.ParameterTypesAndNames})");
            using (writer.Scope) {
                writer.AssignDefaultValuesToOutParameters(info.DeclaringType.Namespace, info.GetParameters());

                writer.Write($"if ({call} != null)");
                using (writer.Scope) {
                    writer.Write($"{method.ReturnClause}{call}({method.ParameterNames});");
                }
                if (method.ReturnType != "void")
                {
                    writer.Write("else");
                    using (writer.Scope) {
                        writer.Write($"return default({method.ReturnType});");
                    }
                }
            }
        }
Exemplo n.º 16
0
        public void AppendItemProperty(PropertyInfo info, MemberMetadata metadata)
        {
            var methodInfo = info.GetMethod ?? info.SetMethod;

            if (methodInfo.IsStatic || methodInfo.IsPrivate || methodInfo.IsAssembly || methodInfo.IsFamilyAndAssembly)
            {
                return;
            }
            if (!methodInfo.IsVirtual && !methodInfo.IsAbstract)
            {
                // the member is protected. Make a public version.
                if (metadata.Access == "protected")
                {
                    intermediateWriter.Write($"public new {metadata.ReturnType} this[{metadata.ParameterTypesAndNames}]");
                    using (intermediateWriter.Scope) {
                        if (info.CanRead)
                        {
                            intermediateWriter.Write($"get {{ return base[{metadata.ParameterNames}]; }}");
                        }
                        if (info.CanWrite)
                        {
                            intermediateWriter.Write($"set {{ base[{metadata.ParameterNames}] = value; }}");
                        }
                    }
                }
                return;
            }

            if (info.CanRead)
            {
                stubWriter.Write($"public new Func<{metadata.ParameterTypes}, {metadata.ReturnType}> get_Item;");
            }
            if (info.CanWrite)
            {
                stubWriter.Write($"public new Action<{metadata.ParameterTypes}, {metadata.ReturnType}> set_Item;");
            }

            if (methodInfo.IsVirtual && !methodInfo.IsAbstract)
            {
                if (info.CanRead)
                {
                    intermediateWriter.Write($"public {metadata.ReturnType} Base_get_Item({metadata.ParameterTypesAndNames})");
                    using (intermediateWriter.Scope) {
                        intermediateWriter.Write($"return base[{metadata.ParameterNames}];");
                    }
                }
                if (info.CanWrite)
                {
                    intermediateWriter.Write($"public void Base_set_Item({metadata.ParameterTypesAndNames}, {metadata.ReturnType} value)");
                    using (intermediateWriter.Scope) {
                        intermediateWriter.Write($"base[{metadata.ParameterNames}] = value;");
                    }
                }
            }

            intermediateWriter.Write($"{metadata.Access} override {metadata.ReturnType} this[{metadata.ParameterTypesAndNames}]");
            using (intermediateWriter.Scope) {
                if (info.CanRead)
                {
                    intermediateWriter.Write($"get {{ return (({stubTypeName})this).get_Item({metadata.ParameterNames}); }}");
                }
                if (info.CanWrite)
                {
                    intermediateWriter.Write($"set {{ (({stubTypeName})this).set_Item({metadata.ParameterNames}, value); }}");
                }
            }
        }
Exemplo n.º 17
0
        private void AppendToConstructorFromProperty(PropertyInfo info, MemberMetadata metadata, CSharpSourceWriter deferWriter)
        {
            var method = info.GetMethod ?? info.SetMethod;

            if (method.IsAbstract && !method.IsAssembly && !method.IsFamilyAndAssembly)
            {
                if (info.Name == "Item" && info.GetIndexParameters().Length > 0)
                {
                    // item property maps to two methods, get_Item and set_Item
                    // but since the property is abstract, we have no base implementation
                    // so just like methods, give no default values in the constructor (or defer construction call)
                }
                else
                {
                    stubWriter.Write($"if ({metadata.Name} == null) {metadata.Name} = new PropertyImplementation<{metadata.ReturnType}>();");
                    deferWriter.Write($"stub.{metadata.Name} = new PropertyImplementation<{metadata.ReturnType}>();");
                }
            }
            if (method.IsStatic || method.IsPrivate || !method.IsVirtual || method.IsAbstract || method.IsAssembly || method.IsFamilyAndAssembly)
            {
                return;
            }

            if (info.Name == "Item" && info.GetIndexParameters().Length > 0)
            {
                if (info.CanRead)
                {
                    stubWriter.Write($"if (get_Item == null) get_Item = Base_get_Item;");
                }
                if (CanWrite(info))
                {
                    stubWriter.Write($"if (set_Item == null) set_Item = Base_set_Item;");
                }
                if (info.CanRead)
                {
                    deferWriter.Write($"stub.get_Item = stub.Base_get_Item;");
                }
                if (CanWrite(info))
                {
                    deferWriter.Write($"stub.set_Item = stub.Base_set_Item;");
                }
            }
            else
            {
                stubWriter.Write($"if ({metadata.Name} == null)");
                using (stubWriter.Scope) {
                    stubWriter.Write($"{metadata.Name} = new PropertyImplementation<{metadata.ReturnType}>();");
                    if (info.CanRead)
                    {
                        stubWriter.Write($"{metadata.Name}.get = () => Base{metadata.Name};");
                    }
                    if (CanWrite(info))
                    {
                        stubWriter.Write($"{metadata.Name}.set = value => Base{metadata.Name} = value;");
                    }
                }
                deferWriter.Write($"stub.{metadata.Name} = new PropertyImplementation<{metadata.ReturnType}>();");
                if (info.CanRead)
                {
                    deferWriter.Write($"stub.{metadata.Name}.get = () => stub.Base{metadata.Name};");
                }
                if (CanWrite(info))
                {
                    deferWriter.Write($"stub.{metadata.Name}.set = value => stub.Base{metadata.Name} = value;");
                }
            }
        }