Mock原理學習

同事搓蛋問了我一個問題,mock的原理是啥,沒怎麼想出來,因而花了點時間學習了一下。git

從Moq這個庫入手:https://github.com/moq/moq4github

Moq用到了Castle的庫用於DynamicProxy的生成和interception,Castle還有IOC的功能,由於每次生成DynamicProxy比較耗時,因此利用IOC還能夠作管理和緩存。shell

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方法去初始化app

public object Object
{
    get { return this.GetObject(); }
}

private object GetObject()
{
    var value = this.OnGetObject();//這是個抽象方法
    this.isInitialized = true;
    return value;
}

而後調用子類的OnGetObject()模板方法less

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是什麼呢?ide

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有好多重載的方法wordpress

#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

相關文章
相關標籤/搜索