public static MethodMetadata Create(
            IProxyDefinitionElement definitionElement, 
            FieldMetadata targetField, 
            MethodInfo method)
        {
            var classProxyDefinition = definitionElement as ClassProxyDefinition;

            if (classProxyDefinition != null)
            {
                return new ClassMethodMetadata(method);
            }

            var interfaceProxyDefinition = definitionElement as InterfaceProxyDefinition;

            if (interfaceProxyDefinition != null)
            {
                return new NonTargetedMethodMetadata(method);
            }

            var targetedClassProxyDefinition = definitionElement as TargetedClassProxyDefinition;

            if (targetedClassProxyDefinition != null)
            {
                var targetMethod = MemberLocator.LocateMatchingMethod(method, targetedClassProxyDefinition.TargetType);

                return new TargetedMethodMetadata(method, targetField, targetMethod);
            }

            var targetedInterfaceProxyDefinition = definitionElement as TargetedInterfaceProxyDefinition;

            if (targetedInterfaceProxyDefinition != null)
            {
                var targetMethod = MemberLocator.LocateMatchingMethod(method, targetedInterfaceProxyDefinition.TargetType);

                return new TargetedMethodMetadata(method, targetField, targetMethod);
            }

            var mixinInterfaveDefinition = definitionElement as MixinInterfaceDefinition;

            if (mixinInterfaveDefinition != null)
            {
                var targetMethod = MemberLocator.LocateMatchingMethod(method, mixinInterfaveDefinition.MixinType);

                return new TargetedMethodMetadata(method, targetField, targetMethod);
            }

            var nonTargetefInterfaceDefinition = definitionElement as NonTargetedInterfaceDefinition;

            if (nonTargetefInterfaceDefinition != null)
            {
                return new NonTargetedMethodMetadata(method);
            }

            throw new NotSupportedException();
        }
        private MethodMetadata CreateMethodMetadata(
            IProxyDefinitionElement definition, 
            FieldMetadata targetField,
            MethodInfo method, 
            bool implementExplicitly)
        {
            var result = MethodMetadataFactory.Create(definition, targetField, method);

            if (implementExplicitly)
            {
                result.UseExplicitInterfaceImplementation();
            }

            _methods.Add(result);

            return result;
        }
        protected void InitializeDefinitionProperties(
            IProxyDefinitionElement definition, 
            FieldMetadata targetField,
            bool implementExplicitly)
        {
            var properties = definition.Type.GetProperties(ProxyBindingFlags);

            for (var i = 0; i < properties.Length; i++)
            {
                var property = properties[i];

                var getMethod = property.GetGetMethod();
                var setMethod = property.GetSetMethod();

                MethodMetadata getMethodMetadata = null;
                MethodMetadata setMethodMetadata = null;

                if (getMethod != null && getMethod.IsVirtual)
                {
                    getMethodMetadata = CreateMethodMetadata(definition, targetField, getMethod, implementExplicitly);
                }

                if (setMethod != null && setMethod.IsVirtual)
                {
                    setMethodMetadata = CreateMethodMetadata(definition, targetField, setMethod, implementExplicitly);
                }

                if (getMethodMetadata != null || setMethodMetadata != null)
                {
                    _properties.Add(new PropertyMetadata(properties[i], getMethodMetadata, setMethodMetadata));
                }
            }
        }
        protected void InitializeDefinitionMethods(
            IProxyDefinitionElement definition, 
            FieldMetadata targetField, 
            bool implementExplicitly)
        {
            var definitionMethods = definition.Type.GetMethods(ProxyBindingFlags);

            for (var i = 0; i < definitionMethods.Length; i++)
            {
                var method = definitionMethods[i];

                if (ShouldBuildMethodMetadata(method))
                {
                    CreateMethodMetadata(definition, targetField, method, implementExplicitly);
                }
            }
        }
        protected virtual void InitializeDefinitionEvents(
            IProxyDefinitionElement definition, 
            FieldMetadata targetField, 
            bool implementExplicitly)
        {
            var events = definition.Type.GetEvents(ProxyBindingFlags);

            for (var i = 0; i < events.Length; i++)
            {
                var @event = events[i];

                var addMethod = @event.GetAddMethod();
                var removeMethod = @event.GetRemoveMethod();
                var raiseMethod = @event.GetRaiseMethod();

                MethodMetadata addMethodMetadata = null;
                MethodMetadata removeMethodMetadata = null;
                MethodMetadata raiseMethodMetadata = null;

                if (addMethod.IsVirtual)
                {
                    addMethodMetadata = CreateMethodMetadata(definition, targetField, addMethod, implementExplicitly);
                }

                if (removeMethod.IsVirtual)
                {
                    removeMethodMetadata = CreateMethodMetadata(definition, targetField, removeMethod, implementExplicitly);
                }

                if (raiseMethod != null && raiseMethod.IsVirtual)
                {
                    raiseMethodMetadata = CreateMethodMetadata(definition, targetField, raiseMethod, implementExplicitly);
                }

                if (addMethodMetadata != null ||
                    removeMethodMetadata != null ||
                    raiseMethodMetadata != null)
                {
                    _events.Add(new EventMetadata(events[i], addMethodMetadata, removeMethodMetadata, raiseMethodMetadata));
                }
            }
        }