zoukankan      html  css  js  c++  java
  • Mock原理学习

    同事搓蛋问了我一个问题,mock的原理是啥,没怎么想出来,于是花了点时间学习了一下。

    从Moq这个库入手:https://github.com/moq/moq4

    Moq用到了Castle的库用于DynamicProxy的生成和interception,Castle还有IOC的功能,因为每次生成DynamicProxy比较耗时,所以利用IOC还可以做管理和缓存。

    Interceptor是一种AOP(Aspect Oriented Programming)思想的实现,Castle里面就是利用了IInterceptor的接口

    namespace Castle.DynamicProxy
    {
        /// <summary>
        ///   New interface that is going to be used by DynamicProxy 2
        /// </summary>
        public interface IInterceptor
        {
            void Intercept(IInvocation invocation);
        }
    }
    public class MyIntercept : IInterceptor {
        public void Intercept(IInvocation invocation) {
            Console.WriteLine(">> intercepted in <<");
            invocation.Proceed();
            Console.WriteLine(">> intercepted out <<");
        }
    }

    你去Mock<T>一个对象实际上就是用到了public partial class Mock<T> : Mock, IMock<T> where T : class的构造函数,当你访问Object对象的时候,实际上调用了GetObject方法去初始化

    public object Object
    {
        get { return this.GetObject(); }
    }
    
    private object GetObject()
    {
        var value = this.OnGetObject();//这是个抽象方法
        this.isInitialized = true;
        return value;
    }

    然后调用子类的OnGetObject()模板方法

    protected override object OnGetObject()
    {
        if (this.instance == null)
        {
            this.InitializeInstance();
        }
    
        return this.instance;
    }
    private void InitializeInstance()
    {
        PexProtector.Invoke(() =>
        {
            if (this.IsDelegateMock)
            {
                // We're mocking a delegate.
                // Firstly, get/create an interface with a method whose signature
                // matches that of the delegate.
                var delegateInterfaceType = proxyFactory.GetDelegateProxyInterface(typeof(T), out delegateInterfaceMethod);
    
                // Then create a proxy for that.
                var delegateProxy = proxyFactory.CreateProxy(
                    delegateInterfaceType,
                    this.Interceptor,
                    this.ImplementedInterfaces.ToArray(),
                    this.constructorArguments);
    
                // Then our instance is a delegate of the desired type, pointing at the
                // appropriate method on that proxied interface instance.
                this.instance = (T)(object)Delegate.CreateDelegate(typeof(T), delegateProxy, delegateInterfaceMethod);
            }
            else
            {
                this.instance = (T)proxyFactory.CreateProxy(
                    typeof(T),
                    this.Interceptor,
                    this.ImplementedInterfaces.ToArray(),
                    this.constructorArguments);
            }
        });
    }

    proxyFactory是什么呢?

    internal class CastleProxyFactory : IProxyFactory
    {
        private static readonly ProxyGenerator generator = CreateProxyGenerator();
    
        [SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "By Design")]
        static CastleProxyFactory()
        {
    #pragma warning disable 618
            AttributesToAvoidReplicating.Add<SecurityPermissionAttribute>();
    #pragma warning restore 618
    
    #if !SILVERLIGHT
            AttributesToAvoidReplicating.Add<ReflectionPermissionAttribute>();
            AttributesToAvoidReplicating.Add<PermissionSetAttribute>();
            AttributesToAvoidReplicating.Add<System.Runtime.InteropServices.MarshalAsAttribute>();
            AttributesToAvoidReplicating.Add<UIPermissionAttribute>();
    #if !NET3x
            AttributesToAvoidReplicating.Add<System.Runtime.InteropServices.TypeIdentifierAttribute>();
    #endif
    #endif
            proxyOptions = new ProxyGenerationOptions { Hook = new ProxyMethodHook(), BaseTypeForInterfaceProxy = typeof(InterfaceProxy) };
        }
    
        /// <inheritdoc />
        public object CreateProxy(Type mockType, ICallInterceptor interceptor, Type[] interfaces, object[] arguments)
        {
            if (mockType.IsInterface) {
                return generator.CreateInterfaceProxyWithoutTarget(mockType, interfaces, proxyOptions, new Interceptor(interceptor));
            }
    
            try
            {
                return generator.CreateClassProxy(mockType, interfaces, proxyOptions, arguments, new Interceptor(interceptor));
            }
            catch (TypeLoadException e)
            {
                throw new ArgumentException(Resources.InvalidMockClass, e);
            }
            catch (MissingMethodException e)
            {
                throw new ArgumentException(Resources.ConstructorNotFound, e);
            }
        }
    
        private static readonly Dictionary<Type, Type> delegateInterfaceCache = new Dictionary<Type, Type>();
        private static readonly ProxyGenerationOptions proxyOptions;
        private static int delegateInterfaceSuffix;
    
        /// <inheritdoc />
        public Type GetDelegateProxyInterface(Type delegateType, out MethodInfo delegateInterfaceMethod)
        {
            Type delegateInterfaceType;
    
            lock (this)
            {
                if (!delegateInterfaceCache.TryGetValue(delegateType, out delegateInterfaceType))
                {
                    var interfaceName = String.Format(CultureInfo.InvariantCulture, "DelegateInterface_{0}_{1}",
                                                      delegateType.Name, delegateInterfaceSuffix++);
    
                    var moduleBuilder = generator.ProxyBuilder.ModuleScope.ObtainDynamicModule(true);
                    var newTypeBuilder = moduleBuilder.DefineType(interfaceName,
                                                                  TypeAttributes.Public | TypeAttributes.Interface |
                                                                  TypeAttributes.Abstract);
    
                    var invokeMethodOnDelegate = delegateType.GetMethod("Invoke");
                    var delegateParameterTypes = invokeMethodOnDelegate.GetParameters().Select(p => p.ParameterType).ToArray();
    
                    // Create a method on the interface with the same signature as the delegate.
                    newTypeBuilder.DefineMethod("Invoke",
                                                MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.Abstract,
                                                CallingConventions.HasThis,
                                                invokeMethodOnDelegate.ReturnType, delegateParameterTypes);
    
                    delegateInterfaceType = newTypeBuilder.CreateType();
                    delegateInterfaceCache[delegateType] = delegateInterfaceType;
                }
            }
    
            delegateInterfaceMethod = delegateInterfaceType.GetMethod("Invoke");
            return delegateInterfaceType;
        }
    
        private static ProxyGenerator CreateProxyGenerator()
        {
            return new ProxyGenerator();
        }
    
        private class Interceptor : IInterceptor
        {
            private ICallInterceptor interceptor;
    
            internal Interceptor(ICallInterceptor interceptor)
            {
                this.interceptor = interceptor;
            }
    
            public void Intercept(IInvocation invocation)
            {
                this.interceptor.Intercept(new CallContext(invocation));
            }
        }
    
        private class CallContext : ICallContext
        {
            private IInvocation invocation;
    
            internal CallContext(IInvocation invocation)
            {
                this.invocation = invocation;
            }
    
            public object[] Arguments
            {
                get { return this.invocation.Arguments; }
            }
    
            public MethodInfo Method
            {
                get { return this.invocation.Method; }
            }
    
            public object ReturnValue
            {
                get { return this.invocation.ReturnValue; }
                set { this.invocation.ReturnValue = value; }
            }
    
            public void InvokeBase()
            {
                this.invocation.Proceed();
            }
    
            public void SetArgumentValue(int index, object value)
            {
                this.invocation.SetArgumentValue(index, value);
            }
        }
    }

    ProxyGenerator就是Castle的一个DynamicProxy的实现,CreateInterfaceProxyWithoutTarget和CreateClassProxy有好多重载的方法

    #region CreateInterfaceProxyWithoutTarget
            
    /// <summary>
    /// Creates proxy object intercepting calls to members of interface <typeparamref name="TInterface"/> on target object generated at runtime with given <paramref name="interceptor"/>.
    /// </summary>
    /// <typeparam name="TInterface">Type of the interface which will be proxied.</typeparam>
    /// <param name="interceptor">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// Object proxying calls to members of <typeparamref name="TInterface"/> types on generated target object.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interceptor"/> array is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <typeparamref name="TInterface"/> is not an interface type.</exception>
    /// <remarks>
    /// Since this method uses an empty-shell implementation of interfaces to proxy generated at runtime, the actual implementation of proxied methods must be provided by given <see cref="IInterceptor"/> implementations.
    /// They are responsible for setting return value (and out parameters) on proxied methods. It is also illegal for an interceptor to call <see cref="IInvocation.Proceed"/>, since there's no actual implementation to proceed with.
    /// As a result of that also at least one <see cref="IInterceptor"/> implementation must be provided.
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public TInterface CreateInterfaceProxyWithoutTarget<TInterface>(IInterceptor interceptor) where TInterface : class
    {
        return (TInterface)CreateInterfaceProxyWithoutTarget(typeof(TInterface), interceptor);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to members of interface <typeparamref name="TInterface"/> on target object generated at runtime with given <paramref name="interceptors"/>.
    /// </summary>
    /// <typeparam name="TInterface">Type of the interface which will be proxied.</typeparam>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// Object proxying calls to members of <typeparamref name="TInterface"/> types on generated target object.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interceptors"/> array is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <typeparamref name="TInterface"/> is not an interface type.</exception>
    /// <remarks>
    /// Since this method uses an empty-shell implementation of interfaces to proxy generated at runtime, the actual implementation of proxied methods must be provided by given <see cref="IInterceptor"/> implementations.
    /// They are responsible for setting return value (and out parameters) on proxied methods. It is also illegal for an interceptor to call <see cref="IInvocation.Proceed"/>, since there's no actual implementation to proceed with.
    /// As a result of that also at least one <see cref="IInterceptor"/> implementation must be provided.
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public TInterface CreateInterfaceProxyWithoutTarget<TInterface>(params IInterceptor[] interceptors) where TInterface : class
    {
        return (TInterface)CreateInterfaceProxyWithoutTarget(typeof(TInterface), interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to members of interface <typeparamref name="TInterface"/> on target object generated at runtime with given <paramref name="interceptors"/>.
    /// </summary>
    /// <typeparam name="TInterface">Type of the interface which will be proxied.</typeparam>
    /// <param name="options">The proxy generation options used to influence generated proxy type and object.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// Object proxying calls to members of <typeparamref name="TInterface"/> types on generated target object.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interceptors"/> array is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <typeparamref name="TInterface"/> is not an interface type.</exception>
    /// <remarks>
    /// Since this method uses an empty-shell implementation of interfaces to proxy generated at runtime, the actual implementation of proxied methods must be provided by given <see cref="IInterceptor"/> implementations.
    /// They are responsible for setting return value (and out parameters) on proxied methods. It is also illegal for an interceptor to call <see cref="IInvocation.Proceed"/>, since there's no actual implementation to proceed with.
    /// As a result of that also at least one <see cref="IInterceptor"/> implementation must be provided.
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public TInterface CreateInterfaceProxyWithoutTarget<TInterface>(ProxyGenerationOptions options, params IInterceptor[] interceptors) where TInterface : class
    {
        return (TInterface)CreateInterfaceProxyWithoutTarget(typeof(TInterface), Type.EmptyTypes, options, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to members of interface <paramref name="interfaceToProxy"/> on target object generated at runtime with given <paramref name="interceptor"/>.
    /// </summary>
    /// <param name="interfaceToProxy">Type of the interface which will be proxied.</param>
    /// <param name="interceptor">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// Object proxying calls to members of <paramref name="interfaceToProxy"/> type on generated target object.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interfaceToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interceptor"/> array is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="interfaceToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="interfaceToProxy"/> is not an interface type.</exception>
    /// <remarks>
    /// Since this method uses an empty-shell implementation of interfaces to proxy generated at runtime, the actual implementation of proxied methods must be provided by given <see cref="IInterceptor"/> implementations.
    /// They are responsible for setting return value (and out parameters) on proxied methods. It is also illegal for an interceptor to call <see cref="IInvocation.Proceed"/>, since there's no actual implementation to proceed with.
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, IInterceptor interceptor)
    {
        return CreateInterfaceProxyWithoutTarget(interfaceToProxy, Type.EmptyTypes, ProxyGenerationOptions.Default, interceptor);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to members of interface <paramref name="interfaceToProxy"/> on target object generated at runtime with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="interfaceToProxy">Type of the interface which will be proxied.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// Object proxying calls to members of <paramref name="interfaceToProxy"/> type on generated target object.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interfaceToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interceptors"/> array is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="interfaceToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="interfaceToProxy"/> is not an interface type.</exception>
    /// <remarks>
    /// Since this method uses an empty-shell implementation of interfaces to proxy generated at runtime, the actual implementation of proxied methods must be provided by given <see cref="IInterceptor"/> implementations.
    /// They are responsible for setting return value (and out parameters) on proxied methods. It is also illegal for an interceptor to call <see cref="IInvocation.Proceed"/>, since there's no actual implementation to proceed with.
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, params IInterceptor[] interceptors)
    {
        return CreateInterfaceProxyWithoutTarget(interfaceToProxy, Type.EmptyTypes, ProxyGenerationOptions.Default, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to members of interface <paramref name="interfaceToProxy"/> on target object generated at runtime with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="interfaceToProxy">Type of the interface which will be proxied.</param>
    /// <param name="additionalInterfacesToProxy">Additional interface types. Calls to their members will be proxied as well.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// Object proxying calls to members of <paramref name="interfaceToProxy"/> and <paramref name="additionalInterfacesToProxy"/> types on generated target object.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interfaceToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interceptors"/> array is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="interfaceToProxy"/> or any of <paramref name="additionalInterfacesToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="interfaceToProxy"/> is not an interface type.</exception>
    /// <remarks>
    /// Since this method uses an empty-shell implementation of interfaces to proxy generated at runtime, the actual implementation of proxied methods must be provided by given <see cref="IInterceptor"/> implementations.
    /// They are responsible for setting return value (and out parameters) on proxied methods. It is also illegal for an interceptor to call <see cref="IInvocation.Proceed"/>, since there's no actual implementation to proceed with.
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, params IInterceptor[] interceptors)
    {
        return CreateInterfaceProxyWithoutTarget(interfaceToProxy, additionalInterfacesToProxy, ProxyGenerationOptions.Default, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to members of interface <paramref name="interfaceToProxy"/> on target object generated at runtime with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="interfaceToProxy">Type of the interface which will be proxied.</param>
    /// <param name="options">The proxy generation options used to influence generated proxy type and object.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// Object proxying calls to members of <paramref name="interfaceToProxy"/> on generated target object.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interfaceToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interceptors"/> array is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="interfaceToProxy"/>  is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="interfaceToProxy"/> is not an interface type.</exception>
    /// <remarks>
    /// They are responsible for setting return value (and out parameters) on proxied methods. It is also illegal for an interceptor to call <see cref="IInvocation.Proceed"/>, since there's no actual implementation to proceed with.
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, ProxyGenerationOptions options, params IInterceptor[] interceptors)
    {
        return CreateInterfaceProxyWithoutTarget(interfaceToProxy, Type.EmptyTypes, options, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to members of interface <paramref name="interfaceToProxy"/> on target object generated at runtime with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="interfaceToProxy">Type of the interface which will be proxied.</param>
    /// <param name="options">The proxy generation options used to influence generated proxy type and object.</param>
    /// <param name="additionalInterfacesToProxy">Additional interface types. Calls to their members will be proxied as well.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// Object proxying calls to members of <paramref name="interfaceToProxy"/> and <paramref name="additionalInterfacesToProxy"/> types on generated target object.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interfaceToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="interceptors"/> array is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="interfaceToProxy"/> or any of <paramref name="additionalInterfacesToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="interfaceToProxy"/> is not an interface type.</exception>
    /// <remarks>
    /// Since this method uses an empty-shell implementation of <paramref name="additionalInterfacesToProxy"/> to proxy generated at runtime, the actual implementation of proxied methods must be provided by given <see cref="IInterceptor"/> implementations.
    /// They are responsible for setting return value (and out parameters) on proxied methods. It is also illegal for an interceptor to call <see cref="IInvocation.Proceed"/>, since there's no actual implementation to proceed with.
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateInterfaceProxyWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, params IInterceptor[] interceptors)
    {
        if (interfaceToProxy == null)
        {
            throw new ArgumentNullException("interfaceToProxy");
        }
        if (interceptors == null)
        {
            throw new ArgumentNullException("interceptors");
        }
    
        if (!interfaceToProxy.IsInterface)
        {
            throw new ArgumentException("Specified type is not an interface", "interfaceToProxy");
        }
    
        CheckNotGenericTypeDefinition(interfaceToProxy, "interfaceToProxy");
        CheckNotGenericTypeDefinitions(additionalInterfacesToProxy, "additionalInterfacesToProxy");
    
        Type generatedType = CreateInterfaceProxyTypeWithoutTarget(interfaceToProxy, additionalInterfacesToProxy, options);
        List<object> arguments = GetConstructorArguments(new object(), interceptors, options);
        return Activator.CreateInstance(generatedType, arguments.ToArray());
    }
    
    #endregion
    #region CreateClassProxy
    /// <summary>
    /// Creates proxy object intercepting calls to virtual members of type <typeparamref name="TClass"/> on newly created instance of that type with given <paramref name="interceptors"/>.
    /// </summary>
    /// <typeparam name="TClass">Type of class which will be proxied.</typeparam>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// New object of type <typeparamref name="TClass"/> proxying calls to virtual members of <typeparamref name="TClass"/> type.
    /// </returns>
    /// <exception cref="ArgumentException">Thrown when given <typeparamref name="TClass"/> is not a class type.</exception>
    /// <exception cref="ArgumentException">Thrown when no default constructor exists on type <typeparamref name="TClass"/>.</exception>
    /// <exception cref="TargetInvocationException">Thrown when default constructor of type <typeparamref name="TClass"/> throws an exception.</exception>
    /// <remarks>
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public TClass CreateClassProxy<TClass>(params IInterceptor[] interceptors) where TClass : class
    {
        return (TClass) CreateClassProxy(typeof(TClass), ProxyGenerationOptions.Default, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to virtual members of type <typeparamref name="TClass"/> on newly created instance of that type with given <paramref name="interceptors"/>.
    /// </summary>
    /// <typeparam name="TClass">Type of class which will be proxied.</typeparam>
    /// <param name="options">The proxy generation options used to influence generated proxy type and object.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// New object of type <typeparamref name="TClass"/> proxying calls to virtual members of <typeparamref name="TClass"/> type.
    /// </returns>
    /// <exception cref="ArgumentException">Thrown when given <typeparamref name="TClass"/> is not a class type.</exception>
    /// <exception cref="ArgumentException">Thrown when no default constructor exists on type <typeparamref name="TClass"/>.</exception>
    /// <exception cref="TargetInvocationException">Thrown when default constructor of type <typeparamref name="TClass"/> throws an exception.</exception>
    /// <remarks>
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public TClass CreateClassProxy<TClass>(ProxyGenerationOptions options, params IInterceptor[] interceptors) where TClass : class
    {
        return (TClass)CreateClassProxy(typeof(TClass), options, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to virtual members of type <paramref name="classToProxy"/> on newly created instance of that type with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="classToProxy">Type of class which will be proxied.</param>
    /// <param name="additionalInterfacesToProxy">Additional interface types. Calls to their members will be proxied as well.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// New object of type <paramref name="classToProxy"/> proxying calls to virtual members of <paramref name="classToProxy"/> and <paramref name="additionalInterfacesToProxy"/> types.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="classToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> or any of <paramref name="additionalInterfacesToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is not a class type.</exception>
    /// <exception cref="ArgumentException">Thrown when no default constructor exists on type <paramref name="classToProxy"/>.</exception>
    /// <exception cref="TargetInvocationException">Thrown when default constructor of type <paramref name="classToProxy"/> throws an exception.</exception>
    /// <remarks>
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, params IInterceptor[] interceptors)
    {
        return CreateClassProxy(classToProxy, additionalInterfacesToProxy, ProxyGenerationOptions.Default, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to virtual members of type <paramref name="classToProxy"/> on newly created instance of that type with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="classToProxy">Type of class which will be proxied.</param>
    /// <param name="constructorArguments">Arguments of constructor of type <paramref name="classToProxy"/> which should be used to create a new instance of that type.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// New object of type <paramref name="classToProxy"/> proxying calls to virtual members of <paramref name="classToProxy"/> type.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="classToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is not a class type.</exception>
    /// <exception cref="ArgumentException">Thrown when no constructor exists on type <paramref name="classToProxy"/> with parameters matching <paramref name="constructorArguments"/>.</exception>
    /// <exception cref="TargetInvocationException">Thrown when constructor of type <paramref name="classToProxy"/> throws an exception.</exception>
    /// <remarks>
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    [Obsolete("This method has been made obsolete due to issues with passing constructor arguments as 'params' array. Use other overload that passes constructor arguments as an explicit array.")]
    public object CreateClassProxy(Type classToProxy, IInterceptor[] interceptors, params object[] constructorArguments)
    {
        return CreateClassProxy(classToProxy, null, ProxyGenerationOptions.Default,
                                constructorArguments, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to virtual members of type <paramref name="classToProxy"/> on newly created instance of that type with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="classToProxy">Type of class which will be proxied.</param>
    /// <param name="options">The proxy generation options used to influence generated proxy type and object.</param>
    /// <param name="constructorArguments">Arguments of constructor of type <paramref name="classToProxy"/> which should be used to create a new instance of that type.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// New object of type <paramref name="classToProxy"/> proxying calls to virtual members of <paramref name="classToProxy"/> type.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="classToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is not a class type.</exception>
    /// <exception cref="ArgumentException">Thrown when no constructor exists on type <paramref name="classToProxy"/> with parameters matching <paramref name="constructorArguments"/>.</exception>
    /// <exception cref="TargetInvocationException">Thrown when constructor of type <paramref name="classToProxy"/> throws an exception.</exception>
    /// <remarks>
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateClassProxy(Type classToProxy, ProxyGenerationOptions options, object[] constructorArguments, params IInterceptor[] interceptors)
    {
        return CreateClassProxy(classToProxy, null, options, constructorArguments, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to virtual members of type <paramref name="classToProxy"/> on newly created instance of that type with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="classToProxy">Type of class which will be proxied.</param>
    /// <param name="constructorArguments">Arguments of constructor of type <paramref name="classToProxy"/> which should be used to create a new instance of that type.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// New object of type <paramref name="classToProxy"/> proxying calls to virtual members of <paramref name="classToProxy"/> type.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="classToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is not a class type.</exception>
    /// <exception cref="ArgumentException">Thrown when no constructor exists on type <paramref name="classToProxy"/> with parameters matching <paramref name="constructorArguments"/>.</exception>
    /// <exception cref="TargetInvocationException">Thrown when constructor of type <paramref name="classToProxy"/> throws an exception.</exception>
    /// <remarks>
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateClassProxy(Type classToProxy, object[] constructorArguments, params IInterceptor[] interceptors)
    {
        return CreateClassProxy(classToProxy, null, ProxyGenerationOptions.Default, constructorArguments, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to virtual members of type <paramref name="classToProxy"/> on newly created instance of that type with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="classToProxy">Type of class which will be proxied.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// New object of type <paramref name="classToProxy"/> proxying calls to virtual members of <paramref name="classToProxy"/> type.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="classToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is not a class type.</exception>
    /// <exception cref="ArgumentException">Thrown when no parameterless constructor exists on type <paramref name="classToProxy"/>.</exception>
    /// <exception cref="TargetInvocationException">Thrown when constructor of type <paramref name="classToProxy"/> throws an exception.</exception>
    /// <remarks>
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateClassProxy(Type classToProxy, params IInterceptor[] interceptors)
    {
        return CreateClassProxy(classToProxy, null, ProxyGenerationOptions.Default,
                                null, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to virtual members of type <paramref name="classToProxy"/> on newly created instance of that type with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="classToProxy">Type of class which will be proxied.</param>
    /// <param name="options">The proxy generation options used to influence generated proxy type and object.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// New object of type <paramref name="classToProxy"/> proxying calls to virtual members of <paramref name="classToProxy"/> type.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="classToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="options"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is not a class type.</exception>
    /// <exception cref="ArgumentException">Thrown when no default constructor exists on type <paramref name="classToProxy"/>.</exception>
    /// <exception cref="TargetInvocationException">Thrown when default constructor of type <paramref name="classToProxy"/> throws an exception.</exception>
    /// <remarks>
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateClassProxy(Type classToProxy, ProxyGenerationOptions options, params IInterceptor[] interceptors)
    {
        return CreateClassProxy(classToProxy, null, options, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to virtual members of type <paramref name="classToProxy"/> on newly created instance of that type with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="classToProxy">Type of class which will be proxied.</param>
    /// <param name="additionalInterfacesToProxy">Additional interface types. Calls to their members will be proxied as well.</param>
    /// <param name="options">The proxy generation options used to influence generated proxy type and object.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// New object of type <paramref name="classToProxy"/> proxying calls to virtual members of <paramref name="classToProxy"/> and <paramref name="additionalInterfacesToProxy"/> types.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="classToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="options"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> or any of <paramref name="additionalInterfacesToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is not a class type.</exception>
    /// <exception cref="ArgumentException">Thrown when no default constructor exists on type <paramref name="classToProxy"/>.</exception>
    /// <exception cref="TargetInvocationException">Thrown when default constructor of type <paramref name="classToProxy"/> throws an exception.</exception>
    /// <remarks>
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, params IInterceptor[] interceptors)
    {
        return CreateClassProxy(classToProxy, additionalInterfacesToProxy, options, null, interceptors);
    }
    
    /// <summary>
    /// Creates proxy object intercepting calls to virtual members of type <paramref name="classToProxy"/> on newly created instance of that type with given <paramref name="interceptors"/>.
    /// </summary>
    /// <param name="classToProxy">Type of class which will be proxied.</param>
    /// <param name="additionalInterfacesToProxy">Additional interface types. Calls to their members will be proxied as well.</param>
    /// <param name="options">The proxy generation options used to influence generated proxy type and object.</param>
    /// <param name="constructorArguments">Arguments of constructor of type <paramref name="classToProxy"/> which should be used to create a new instance of that type.</param>
    /// <param name="interceptors">The interceptors called during the invocation of proxied methods.</param>
    /// <returns>
    /// New object of type <paramref name="classToProxy"/> proxying calls to virtual members of <paramref name="classToProxy"/> and <paramref name="additionalInterfacesToProxy"/> types.
    /// </returns>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="classToProxy"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentNullException">Thrown when given <paramref name="options"/> object is a null reference (Nothing in Visual Basic).</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> or any of <paramref name="additionalInterfacesToProxy"/> is a generic type definition.</exception>
    /// <exception cref="ArgumentException">Thrown when given <paramref name="classToProxy"/> is not a class type.</exception>
    /// <exception cref="ArgumentException">Thrown when no constructor exists on type <paramref name="classToProxy"/> with parameters matching <paramref name="constructorArguments"/>.</exception>
    /// <exception cref="TargetInvocationException">Thrown when constructor of type <paramref name="classToProxy"/> throws an exception.</exception>
    /// <remarks>
    /// This method uses <see cref="IProxyBuilder"/> implementation to generate a proxy type.
    /// As such caller should expect any type of exception that given <see cref="IProxyBuilder"/> implementation may throw.
    /// </remarks>
    public object CreateClassProxy(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options, object[] constructorArguments, params IInterceptor[] interceptors)
    {
        if (classToProxy == null)
        {
            throw new ArgumentNullException("classToProxy");
        }
        if (options == null)
        {
            throw new ArgumentNullException("options");
        }
        if (!classToProxy.IsClass)
        {
            throw new ArgumentException("'classToProxy' must be a class", "classToProxy");
        }
    
        CheckNotGenericTypeDefinition(classToProxy, "classToProxy");
        CheckNotGenericTypeDefinitions(additionalInterfacesToProxy, "additionalInterfacesToProxy");
    
        Type proxyType = CreateClassProxyType(classToProxy, additionalInterfacesToProxy, options);
    
        // create constructor arguments (initialized with mixin implementations, interceptors and target type constructor arguments)
        List<object> arguments = BuildArgumentListForClassProxy(options, interceptors);
        if (constructorArguments != null && constructorArguments.Length != 0)
        {
            arguments.AddRange(constructorArguments);
        }
        return CreateClassProxyInstance(proxyType, arguments, classToProxy, constructorArguments);
    }
    
    private object CreateClassProxyInstance(Type proxyType, List<object> proxyArguments, Type classToProxy, object[] constructorArguments)
    {
        try
        {
            return Activator.CreateInstance(proxyType, proxyArguments.ToArray());
        }
        catch (MissingMethodException)
        {
            var message = new StringBuilder();
            message.AppendFormat("Can not instantiate proxy of class: {0}.", classToProxy.FullName);
            message.AppendLine();
            if (constructorArguments == null || constructorArguments.Length == 0)
            {
                message.Append("Could not find a parameterless constructor.");
            }
            else
            {
                message.AppendLine("Could not find a constructor that would match given arguments:");
                foreach(var argument in constructorArguments)
                {
                    message.AppendLine(argument.GetType().ToString());
                }
            }
            throw new ArgumentException(message.ToString(), "constructorArguments");
        }
    }
    
    private List<object> BuildArgumentListForClassProxy(ProxyGenerationOptions options, IInterceptor[] interceptors)
    {
        var arguments = new List<object>(options.MixinData.Mixins) { interceptors };
        if (options.Selector != null)
        {
            arguments.Add(options.Selector);
        }
        return arguments;
    }
    
    #endregion
    
    /// <summary>
    /// Creates the proxy type for class proxy with given <paramref name="classToProxy"/> class, implementing given <paramref name="additionalInterfacesToProxy"/> and using provided <paramref name="options"/>.
    /// </summary>
    /// <param name="classToProxy">The base class for proxy type.</param>
    /// <param name="additionalInterfacesToProxy">The interfaces that proxy type should implement.</param>
    /// <param name="options">The options for proxy generation process.</param>
    /// <returns><see cref="Type"/> of proxy.</returns>
    protected Type CreateClassProxyType(Type classToProxy, Type[] additionalInterfacesToProxy, ProxyGenerationOptions options)
    {
        // create proxy
        return ProxyBuilder.CreateClassProxyType(classToProxy, additionalInterfacesToProxy, options);
    }
    
    /// <summary>
    /// Creates the proxy type for interface proxy with target for given <paramref name="interfaceToProxy"/> interface, implementing given <paramref name="additionalInterfacesToProxy"/> on given <paramref name="targetType"/> and using provided <paramref name="options"/>.
    /// </summary>
    /// <param name="interfaceToProxy">The interface proxy type should implement.</param>
    /// <param name="additionalInterfacesToProxy">The additional interfaces proxy type should implement.</param>
    /// <param name="targetType">Actual type that the proxy type will encompass.</param>
    /// <param name="options">The options for proxy generation process.</param>
    /// <returns><see cref="Type"/> of proxy.</returns>
    protected Type CreateInterfaceProxyTypeWithTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy, Type targetType,
                                                      ProxyGenerationOptions options)
    {
        // create proxy
        return ProxyBuilder.CreateInterfaceProxyTypeWithTarget(interfaceToProxy, additionalInterfacesToProxy, targetType, options);
    }
    
    /// <summary>
    /// Creates the proxy type for interface proxy with target interface for given <paramref name="interfaceToProxy"/> interface, implementing given <paramref name="additionalInterfacesToProxy"/> on given <paramref name="targetType"/> and using provided <paramref name="options"/>.
    /// </summary>
    /// <param name="interfaceToProxy">The interface proxy type should implement.</param>
    /// <param name="additionalInterfacesToProxy">The additional interfaces proxy type should implement.</param>
    /// <param name="options">The options for proxy generation process.</param>
    /// <returns><see cref="Type"/> of proxy.</returns>
    protected Type CreateInterfaceProxyTypeWithTargetInterface(Type interfaceToProxy, Type[] additionalInterfacesToProxy,
                                                               ProxyGenerationOptions options)
    {
        // create proxy
        return ProxyBuilder.CreateInterfaceProxyTypeWithTargetInterface(interfaceToProxy, additionalInterfacesToProxy, options);
    }
    
    /// <summary>
    /// Creates the proxy type for interface proxy without target for given <paramref name="interfaceToProxy"/> interface, implementing given <paramref name="additionalInterfacesToProxy"/> and using provided <paramref name="options"/>.
    /// </summary>
    /// <param name="interfaceToProxy">The interface proxy type should implement.</param>
    /// <param name="additionalInterfacesToProxy">The additional interfaces proxy type should implement.</param>
    /// <param name="options">The options for proxy generation process.</param>
    /// <returns><see cref="Type"/> of proxy.</returns>
    protected Type CreateInterfaceProxyTypeWithoutTarget(Type interfaceToProxy, Type[] additionalInterfacesToProxy,
                                                         ProxyGenerationOptions options)
    {
        // create proxy
        return ProxyBuilder.CreateInterfaceProxyTypeWithoutTarget(interfaceToProxy, additionalInterfacesToProxy, options);
    }

    当你写UT的时候Mock过的方法就会被拦截调用,否则还是调用自己真正的实现。

    至于说Castle是通过EMIT的方式创建proxy的,而且和CLR的实现方式有所区别,需要进一步研究。

    参考:

    http://app-code.net/wordpress/?p=689

    https://github.com/moq/moq4

    https://github.com/castleproject/Castle.DynamicProxy-READONLY/blob/master/src/Castle.DynamicProxy/ProxyGenerator.cs

    https://github.com/castleproject/Core/blob/master/src/Castle.Core/DynamicProxy/IInterceptor.cs

  • 相关阅读:
    数学建模反思
    [Leetcode]unique binary search trees
    暑假结束了,开始新的学习
    什么是lamda表达式?
    [Java]三大特性之封装
    [Leetcode]003. Longest Substring Without Repeating Characters
    [Leetcode] 002. Add Two Numbers
    [Leetcode] 001.Two Sum
    [数据结构]AVL树
    [数据结构]二叉搜索树
  • 原文地址:https://www.cnblogs.com/cqcmdwym/p/5314712.html
Copyright © 2011-2022 走看看