同事搓蛋问了我一个问题,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 != )
{
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 == )
{
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

Mock原理学习的更多相关文章

  1. IIS原理学习

    IIS 原理学习 首先声明以下内容是我在网上搜索后整理的,在此只是进行记录,以备往后查阅只用. IIS 5.x介绍 IIS 5.x一个显著的特征就是Web Server和真正的ASP.NET Appl ...

  2. zookkeper原理学习

    zookkeper原理学习  https://segmentfault.com/a/1190000014479433   https://www.cnblogs.com/felixzh/p/58692 ...

  3. GIS原理学习目录

    GIS原理学习目录 内容提要 本网络教程是教育部“新世纪网络课程建设工程”的实施课程.系统扼要地阐述地理信息系统的技术体系,重点突出地理信息系统的基本技术及方法. 本网络教程共分八章:第一章绪论,重点 ...

  4. 转:SVM与SVR支持向量机原理学习与思考(一)

    SVM与SVR支持向量机原理学习与思考(一) 转:http://tonysh-thu.blogspot.com/2009/07/svmsvr.html 弱弱的看了看老掉牙的支持向量机(Support ...

  5. Android自复制传播APP原理学习(翻译)

     Android自复制传播APP原理学习(翻译) 1 背景介绍 论文链接:http://arxiv.org/abs/1511.00444 项目地址:https://github.com/Tribler ...

  6. 计算机原理学习(1)-- 冯诺依曼体系和CPU工作原理

    前言 对于我们80后来说,最早接触计算机应该是在95年左右,那个时候最流行的一个词语是多媒体. 依旧记得当时在同学家看同学输入几个DOS命令就成功的打开了一个游戏,当时实在是佩服的五体投地.因为对我来 ...

  7. Dubbo原理学习

    Dubbo源码及原理学习 阿里中间件团队博客 Dubbo官网 Dubbo源码解析 Dubbo源码解析-掘金 Dubbo源码解析-赵计刚 Dubbo系列 源码总结+最近感悟

  8. XGBoost原理学习总结

    XGBoost原理学习总结 前言 ​ XGBoost是一个上限提别高的机器学习算法,和Adaboost.GBDT等都属于Boosting类集成算法.虽然现在深度学习算法大行其道,但很多数据量往往没有太 ...

  9. Git原理学习记录

    Git原理学习记录 1.git init git-test ​ git init 实际上就是在特定的目录下创建对应的目录和文件 2.object $ echo "V1" > ...

随机推荐

  1. Html.Partial和Html. RenderPartial

    圣殿骑士-蓝剑行动 Html.Partial和Html. RenderPartial用法 Html.Partial和Html. RenderPartial用法 Html.partial和RenderP ...

  2. 2136 Largest prime factor(打表)

    Problem Description Everybody knows any number can be combined by the prime number.Now, your task is ...

  3. TableLayout中stretchColumns、shrinkColumns的使用方法

    android:stretchColumns="1" android:shrinkColumns="1"这两个属性是TableLayout所特有的,也是这两个属 ...

  4. ural 1932 The Secret of Identifier 容斥

    主题链接:点击打开链接 stl+容斥 #include <cstdio> #include <cstring> #include <algorithm> #incl ...

  5. vs2012代码段,快捷键,snippet 的使用

    这篇还是介绍怎么简单我们编写代码------本想放在上一篇   插件    一起,但是怕搜不到, 大神们就没法给我教更好的方式,所以就另写了一篇 [大家看完后,插件resharp如果能实现这效果,请教 ...

  6. php_公共方法01_传入数组_打印可见信息

    function decodeUnicode($str) { return preg_replace_callback('/\\\\u([0-9a-f]{4})/i', 'convert', $str ...

  7. String不变性

    String不变性理解类型: String x = "java"; System.out.println(x);//输出为java x.concat("java" ...

  8. java io流之int数组数据的插入与取出

    java io流大家都非常熟悉吧,有时候假设用的不熟,对于数据的处理真的非常头疼,以下是对与int数组的处理. 以下是代码: public class Stream { private int a[] ...

  9. Linux下一个C基本的编程----写进Blog在那之前

    展望2周的实习吧. 各种酸甜苦辣.由于公司只是广告.毛承保让我去.严重的歧视.想也想开,争夺.结果让它成为.还是把它写自己的学习经验,我有同样的希望和迷茫的同学.少走一点弯路.行.切入正题: 一.參考 ...

  10. 【高德地图API】那些年我们一起开发的APP—即LBS应用模式分享

    原文:[高德地图API]那些年我们一起开发的APP—即LBS应用模式分享 摘要:利用地图API都能做些什么应用呢?应用商店里所有的分类,都可以结合上LBS来丰富应用.除了传统的生活服务应用,还有新潮的 ...