private void ProcessNonImplementedMethodsAndPropertiesOnChildrenAdded()
        {
            void processType(Type type, ref bool stopProcessingParam)
            {
                foreach (var methodInfo in type.GetMethods())
                {
                    if ((methodInfo.Attributes & MethodAttributes.SpecialName) == MethodAttributes.SpecialName)
                    {
                        continue;
                    }

                    if (_methodInfoToSimilarOrSameAutoGeneratedServiceMethodElementMap.TryGetValue(methodInfo,
                                                                                                   out var similarOrSameAutoGeneratedServiceMethodElement))
                    {
                        continue;
                    }

                    similarOrSameAutoGeneratedServiceMethodElement = _methods.FirstOrDefault(
                        x => CanMethodBeReplacedWithAnotherMethod(methodInfo, x.ImplementedMehodInfo));

                    if (similarOrSameAutoGeneratedServiceMethodElement != null)
                    {
                        _methodInfoToSimilarOrSameAutoGeneratedServiceMethodElementMap[methodInfo] = similarOrSameAutoGeneratedServiceMethodElement;
                    }
                }

                foreach (var propertyInfo in type.GetProperties())
                {
                    if ((propertyInfo.Attributes & PropertyAttributes.SpecialName) == PropertyAttributes.SpecialName)
                    {
                        continue;
                    }

                    if (_propertyInfoToSimilarOrSameAutoGeneratedServicePropertyElementMap.TryGetValue(propertyInfo,
                                                                                                       out var similarOrSameAutoGeneratedServicePropertyElement))
                    {
                        continue;
                    }

                    similarOrSameAutoGeneratedServicePropertyElement = _properties.FirstOrDefault(
                        x => propertyInfo.PropertyType.IsTypeAssignableFrom(x.ImplementedPropertyInfo.PropertyType));

                    if (similarOrSameAutoGeneratedServicePropertyElement != null)
                    {
                        _propertyInfoToSimilarOrSameAutoGeneratedServicePropertyElementMap[propertyInfo] = similarOrSameAutoGeneratedServicePropertyElement;
                    }
                }
            }

            var stopProcessing = false;

            TypeMemberLookupHelper.ProcessTypeImplementedInterfacesAndBaseTypes(ImplementedInterfaceTypeInfo.Type,
                                                                                processType, ref stopProcessing);
        }
        /*Throws an exception if single method is not found.*/
        private MethodInfo GetMethodInfoInClassOrParents([NotNull] string methodName,
                                                         [NotNull, ItemNotNull] Type[] parameterTypes)
        {
            StringBuilder errorMessage;
            var           matchedMethodInfos = new List <MethodInfo>();

            MethodInfo findMethodInfoInType(Type type)
            {
                foreach (var methodInfo in type.GetMethods())
                {
                    if (Helpers.IsMethodAMatch(methodInfo, methodName, parameterTypes) &&
                        methodInfo.ReturnType == ValueTypeInfo.Type)
                    {
                        return(methodInfo);
                    }
                }

                return(null);
            }

            void appendMethodParametersDescriptionOnInvalidSignature()
            {
                var parametersStrBldr = new StringBuilder();

                parametersStrBldr.Append("Method parameters used for lookup are: ");

                if (parameterTypes.Length == 0)
                {
                    parametersStrBldr.AppendLine("no parameters.");
                }
                else
                {
                    parametersStrBldr.AppendLine(string.Join(", ",
                                                             MethodSignatureElement.Parameters.Select(x => x.ValueTypeInfo.TypeCSharpFullName)));
                }

                errorMessage.AppendLine(parametersStrBldr.ToString());
                errorMessage.AppendLine("Note, currently methods that use in, ref, out keywords are not considered.");
            }

            if (DeclaringTypeInfoInAttribute != null)
            {
                var matchedMethodInfo = findMethodInfoInType(DeclaringTypeInfoInAttribute.Type);

                if (matchedMethodInfo != null)
                {
                    return(matchedMethodInfo);
                }

                errorMessage = new StringBuilder();
                errorMessage.AppendFormat("Method named '{0}', with return type '{1}' and the specified parameters was not found in interface '{2}', specified in  attribute '{3}'.",
                                          methodName, ValueTypeInfo.TypeCSharpFullName, DeclaringTypeInfoInAttribute.TypeCSharpFullName, ConfigurationFileAttributeNames.DeclaringInterface);

                errorMessage.AppendLine();

                appendMethodParametersDescriptionOnInvalidSignature();

                errorMessage.AppendLine("Remove the attribute to look in auto-implemented interface, and all the base interfaces.");
                throw new ConfigurationParseException(this, errorMessage.ToString());
            }

            void processType(Type type, ref bool stopProcessing2)
            {
                var methodInfo = findMethodInfoInType(type);

                if (methodInfo != null)
                {
                    matchedMethodInfos.Add(methodInfo);
                    if (type == ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.Type)
                    {
                        stopProcessing2 = true;
                    }
                }
            }

            var stopProcessing = false;

            TypeMemberLookupHelper.ProcessTypeImplementedInterfacesAndBaseTypes(
                ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.Type,
                processType, ref stopProcessing);

            if (matchedMethodInfos.Count == 1)
            {
                return(matchedMethodInfos[0]);
            }

            errorMessage = new StringBuilder();

            errorMessage.AppendFormat("Method with name '{0}', with return type '{1}' and the specified parameters ",
                                      methodName, ValueTypeInfo.TypeCSharpFullName);

            if (matchedMethodInfos.Count == 0)
            {
                errorMessage.AppendFormat("was not found in auto-generated interface '{0}' or any of its parents.",
                                          ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.TypeCSharpFullName);
                errorMessage.AppendLine();

                appendMethodParametersDescriptionOnInvalidSignature();
            }
            else
            {
                errorMessage.AppendFormat("was not found in auto-generated interface '{0}', however a method with this name, signature, and return type occurs multiple times in parents of interface '{0}'.",
                                          ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.TypeCSharpFullName);
                errorMessage.AppendLine();
                errorMessage.AppendLine($"Please use attribute '{ConfigurationFileAttributeNames.DeclaringInterface}', to explicitly specify the type, where the auto-implemented method is declared.");

                //--Example of proxy service--
                errorMessage.AppendFormat($"Note, if necessary, you can use '{0}' to proxy the service '{1}'.",
                                          ConfigurationFileElementNames.ProxyService, ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.TypeCSharpFullName);
                errorMessage.AppendLine();

                errorMessage.AppendLine(MessagesHelper.GenerateProxyServiceExample(matchedMethodInfos[0].DeclaringType,
                                                                                   ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.Type));

                // List of parent types
                appendMethodParametersDescriptionOnInvalidSignature();
                errorMessage.AppendLine(
                    string.Format("The following is the list of interfaces, where the method was found: {0}",
                                  string.Join(", ", matchedMethodInfos.Select(x => x.DeclaringType.GetTypeNameInCSharpClass()))));
            }

            throw new ConfigurationParseException(this, errorMessage.ToString());
        }
        public override void GenerateAutoImplementedServiceClassCSharp(IDynamicAssemblyBuilder dynamicAssemblyBuilder,
                                                                       string dynamicImplementationsNamespace,
                                                                       out string generatedClassFullName)
        {
            // Add assemblies referenced by service
            var className = $"{ImplementedInterfaceTypeInfo.Type.Name}_{GlobalsCoreAmbientContext.Context.GenerateUniqueId()}";

            generatedClassFullName = $"{dynamicImplementationsNamespace}.{className}";

            var classStrBldr = new StringBuilder(5000);
            var classMemberVariablesStrBldr = new StringBuilder(1000);

            classStrBldr.AppendLine();
            classStrBldr.AppendLine("using System;");

            classStrBldr.AppendLine($"namespace {dynamicImplementationsNamespace}");
            classStrBldr.AppendLine("{");
            classStrBldr.AppendLine($"public sealed class {className}: {ImplementedInterfaceTypeInfo.TypeCSharpFullName}");
            classStrBldr.AppendLine("{");

            void ProcessType(Type type, ref bool stopProcessingParam)
            {
                foreach (var methodInfo in type.GetMethods())
                {
                    if ((methodInfo.Attributes & MethodAttributes.SpecialName) > 0)
                    {
                        continue;
                    }

                    var methodSignature = GetMethodSignature(methodInfo);

                    classStrBldr.Append(methodSignature);

                    if (_methodInfoToSimilarOrSameAutoGeneratedServiceMethodElementMap.TryGetValue(methodInfo, out var autoGeneratedServiceMethodElement))
                    {
                        if (autoGeneratedServiceMethodElement.ImplementedMehodInfo == methodInfo)
                        {
                            AddMethodBodyForImplementedMethod(dynamicAssemblyBuilder, autoGeneratedServiceMethodElement,
                                                              classStrBldr, classMemberVariablesStrBldr);
                        }
                        else
                        {
                            AddMethodBodyForNonImplementedMethodWithSimilarImplementedMethod(dynamicAssemblyBuilder, methodInfo,
                                                                                             autoGeneratedServiceMethodElement, classStrBldr);
                        }
                    }
                    else
                    {
                        classStrBldr.AppendLine("{");

                        foreach (var outParameterInfo in methodInfo.GetParameters().Where(x => x.IsOut))
                        {
                            classStrBldr.AppendLine($"{outParameterInfo.Name}=default({outParameterInfo.ParameterType.GetTypeNameInCSharpClass()});");
                        }

                        if (methodInfo.ReturnType != typeof(void))
                        {
                            classStrBldr.Append($"return default({methodInfo.ReturnType.GetTypeNameInCSharpClass()});");
                            classStrBldr.AppendLine();
                        }

                        classStrBldr.AppendLine("}");
                    }

                    classStrBldr.AppendLine();
                }

                foreach (var propertyInfo in type.GetProperties())
                {
                    var propertyHeader = GetPropertyHeader(propertyInfo);

                    void AddGetSet()
                    {
                        classStrBldr.Append(" { get;");

                        if (propertyInfo.SetMethod != null)
                        {
                            classStrBldr.Append(" set;");
                        }

                        classStrBldr.Append("}");
                    }

                    classStrBldr.Append(propertyHeader);

                    if (_propertyInfoToSimilarOrSameAutoGeneratedServicePropertyElementMap.TryGetValue(propertyInfo,
                                                                                                       out var similarOrSameAutoGeneratedServicePropertyElement))
                    {
                        var similarPropertyInfo = similarOrSameAutoGeneratedServicePropertyElement.ImplementedPropertyInfo;
                        if (similarPropertyInfo == propertyInfo)
                        {
                            AddGetSet();
                            classStrBldr.AppendLine($"={similarOrSameAutoGeneratedServicePropertyElement.ReturnValueElement.GenerateValueCSharp(dynamicAssemblyBuilder)};");
                        }
                        else
                        {
                            var privateVariableName = $"_{propertyInfo.DeclaringType.Name}_{propertyInfo.Name}_{GlobalsCoreAmbientContext.Context.GenerateUniqueId()}";

                            // Not, for nullable values and reference types, we can check for null, to set if the value was initialized.
                            // however, to make it simple, and also to avoid calling the other class property every time, in case the other property
                            // value is null, lets use the valueWasSet variable for all cases.
                            var valueWasSetVariableName = $"{privateVariableName}_ValueWasSet";


                            classStrBldr.AppendLine();
                            classStrBldr.AppendLine("{");

                            classStrBldr.AppendLine("get");
                            classStrBldr.AppendLine("{");

                            classStrBldr.AppendLine($"if ({valueWasSetVariableName}) return {privateVariableName};");

                            classStrBldr.Append($"{privateVariableName}=(({similarPropertyInfo.DeclaringType.GetTypeNameInCSharpClass()})this)");
                            classStrBldr.AppendLine($".{similarPropertyInfo.Name};");
                            classStrBldr.AppendLine($"{valueWasSetVariableName}=true;");
                            classStrBldr.AppendLine($"return {privateVariableName};");
                            classStrBldr.AppendLine("}");

                            if (propertyInfo.SetMethod != null)
                            {
                                classStrBldr.AppendLine("set");
                                classStrBldr.AppendLine("{");
                                classStrBldr.AppendLine($"{privateVariableName}=value;");
                                classStrBldr.AppendLine($"{valueWasSetVariableName}=true;");

                                classStrBldr.AppendLine("}");
                            }

                            classStrBldr.AppendLine("}");
                            classStrBldr.AppendLine($"private bool {valueWasSetVariableName};");
                            classStrBldr.AppendLine($"private {propertyInfo.PropertyType.GetTypeNameInCSharpClass()} {privateVariableName};");
                        }
                    }
                    else
                    {
                        AddGetSet();
                        classStrBldr.AppendLine();
                    }
                }
            }

            var stopProcessing = false;

            TypeMemberLookupHelper.ProcessTypeImplementedInterfacesAndBaseTypes(ImplementedInterfaceTypeInfo.Type,
                                                                                ProcessType, ref stopProcessing);

            classStrBldr.Append(classMemberVariablesStrBldr);

            // Close class
            classStrBldr.AppendLine("}");

            // Close namespace
            classStrBldr.AppendLine("}");

            dynamicAssemblyBuilder.AddCSharpFile(classStrBldr.ToString());
        }
        /*Throws an exception if single method is not found.*/
        private PropertyInfo GetPropertyInfoInClassOrParents([NotNull] string propertyName)
        {
            StringBuilder errorMessage;
            var           matchedPropertyInfos = new List <PropertyInfo>();

            PropertyInfo findPropertyInfoInType(Type type)
            {
                foreach (var propertyInfo in type.GetProperties())
                {
                    if (!propertyInfo.Name.Equals(propertyName, StringComparison.Ordinal))
                    {
                        continue;
                    }

                    var getMethodInfo = propertyInfo.GetMethod;

                    if (getMethodInfo != null && getMethodInfo.IsPublic && getMethodInfo.ReturnType == ValueTypeInfo.Type)
                    {
                        return(propertyInfo);
                    }
                }

                return(null);
            }

            if (DeclaringTypeInfoInAttribute != null)
            {
                var matchedPropertyInfo = findPropertyInfoInType(DeclaringTypeInfoInAttribute.Type);

                if (matchedPropertyInfo != null)
                {
                    return(matchedPropertyInfo);
                }

                errorMessage = new StringBuilder();
                errorMessage.AppendFormat("Property named '{0}', and with return type '{1}' was not found in interface '{2}', specified in  attribute '{3}'.",
                                          propertyName, ValueTypeInfo.TypeCSharpFullName, DeclaringTypeInfoInAttribute.TypeCSharpFullName, ConfigurationFileAttributeNames.DeclaringInterface);

                errorMessage.AppendLine();
                errorMessage.AppendLine("Remove the attribute to look in auto-implemented interface, and all the base interfaces.");
                throw new ConfigurationParseException(this, errorMessage.ToString());
            }

            void processType(Type type, ref bool stopProcessing2)
            {
                var propertyInfo = findPropertyInfoInType(type);

                if (propertyInfo != null)
                {
                    matchedPropertyInfos.Add(propertyInfo);

                    if (type == ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.Type)
                    {
                        stopProcessing2 = true;
                    }
                }
            }

            var stopProcessing = false;

            TypeMemberLookupHelper.ProcessTypeImplementedInterfacesAndBaseTypes(
                ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.Type,
                processType, ref stopProcessing);

            if (matchedPropertyInfos.Count == 1)
            {
                return(matchedPropertyInfos[0]);
            }

            errorMessage = new StringBuilder();

            errorMessage.AppendFormat("Property with name '{0}', and with return type '{1}' ",
                                      propertyName, ValueTypeInfo.TypeCSharpFullName);

            if (matchedPropertyInfos.Count == 0)
            {
                errorMessage.AppendFormat("was not found in auto-generated interface '{0}' or any of its parents.",
                                          ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.TypeCSharpFullName);
                errorMessage.AppendLine();
            }
            else
            {
                errorMessage.AppendFormat("was not found in auto-generated interface '{0}', however a property with this name and return type occurs multiple times in parents of interface '{0}'.",
                                          ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.TypeCSharpFullName);
                errorMessage.AppendLine();
                errorMessage.AppendLine($"Please use attribute '{ConfigurationFileAttributeNames.DeclaringInterface}', to explicitly specify the type, where the auto-implemented property is declared.");

                //--Example of proxy service--
                errorMessage.AppendFormat($"Note, if necessary, you can use '{0}' to proxy the service '{1}'.",
                                          ConfigurationFileElementNames.ProxyService, ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.TypeCSharpFullName);
                errorMessage.AppendLine();

                errorMessage.AppendLine(MessagesHelper.GenerateProxyServiceExample(matchedPropertyInfos[0].DeclaringType,
                                                                                   ParentAutoGeneratedServiceElement.ImplementedInterfaceTypeInfo.Type));

                // List of parent types
                errorMessage.AppendLine(
                    string.Format("The following is the list of interfaces, where the property was found: {0}",
                                  string.Join(", ", matchedPropertyInfos.Select(x => x.DeclaringType.GetTypeNameInCSharpClass()))));
            }

            throw new ConfigurationParseException(this, errorMessage.ToString());
        }