Background
AOP is the aspect-oriented programming, Castle is the most well-known in many AOP frameworks, and there are dead spring.net, of course, the. NET Core Community rookie Aspectcore is excellent in performance and functionality, has gradually been respected by the community and more and more people use. Thanks for the gift of lemon classmate!
If you want to implement an AOP because of your own needs or learning, do you think that you should use emit to do it? Recently I learned that System.Reflection.DispatchProxy this Corefx class library, has implemented the dynamic agent function.
System.Reflection.DispatchProxy
Here's how it's used:
class Program
{
static void Main (string [] args)
{
// Create a proxy class and inject SampleProxy as an interceptor
var sampleProxy = (targetInterface) SampleProxy.Create <targetInterface, SampleProxy> ();
// Execute the interface method
sampleProxy.Write ("here is invoke by proxy");
}
}
// Interface that needs to be generated by proxy instance
public interface targetInterface
{
// This method will be implemented by the proxy class
void Write (string writesomeshing);
}
public class SampleProxy: DispatchProxy
{
/// <summary>
/// intercept the call
/// </ summary>
/// <param name = "method"> Method information intercepted </ param>
/// <param name = "parameters"> The parameters of the intercepted method are passed in. </ param>
/// <returns> </ returns>
protected override object Invoke (MethodInfo targetMethod, object [] args)
{
Console.WriteLine (args [0]);
return null;
}
}
To create a simple AOP why?
System.Reflection.DispatchProxyThere is only one API, that isobjecct Create<T,TProxy>() where TProxy:DispatchProxy, constraining the passing of only generic parameters, and cannot pass in the type from the method, which poses many problems. And more exasperating is, to the official issue, or not to add this API ...
Transformation method
Fortunately, under that issue, the issue author provides a solution to construct this generic method with reflection. I am also on this basis, encapsulated a bit, added the incoming interceptor instance and incoming interceptor construction method parameters of the function.
/// <summary>
/// Interceptor interface
/// </ summary>
public interface IInterceptor
{
/// <summary>
/// Interceptor call
/// </ summary>
/// <param name = "target"> proxy instance </ param>
/// <param name = "method"> the method intercepted </ param>
/// <param name = "parameters"> parameter values passed by the intercepted method </ param>
/// <returns> The return value will be passed to the method return value </ returns>
object Intercept (object target, MethodInfo method, object [] parameters);
}
To implement this interface, the following is the encapsulation of the Dispatchproxy, implementing more methods for creating proxy instances
public class ProxyGenerator: DispatchProxy
{
private IInterceptor interceptor {get; set;}
/// <summary>
/// create a proxy instance
/// </ summary>
/// <param name = "targetType"> Interface type to be proxied </ param>
/// <param name = "interceptor"> Interceptor </ param>
/// <returns> proxy instance </ returns>
public static object Create (Type targetType, IInterceptor interceptor)
{
object proxy = GetProxy (targetType);
((ProxyGenerator) proxy) .CreateInstance (interceptor);
return proxy;
}
/// <summary>
/// create a proxy instance
/// </ summary>
/// <param name = "targetType"> Interface type to be proxied </ param>
/// <param name = "interceptorType"> Interceptor type </ param>
/// <param name = "parameters"> Interceptor constructor parameter values </ param>
/// <returns> proxy instance </ returns>
public static object Create (Type targetType, Type interceptorType, params object [] parameters)
{
object proxy = GetProxy (targetType);
((ProxyGenerator) proxy) .CreateInstance (interceptorType, parameters);
return proxy;
}
/// <summary>
/// Create a proxy instance TTarget: The type of interface to be proxied TInterceptor: Interceptor type
/// </ summary>
/// <param name = "parameters"> Interceptor constructor parameter values </ param>
/// <returns> proxy instance </ returns>
public static TTarget Create <TTarget, TInterceptor> (params object [] parameters) where TInterceptor: IInterceptor
{
var proxy = GetProxy (typeof (TTarget));
((ProxyGenerator) proxy) .CreateInstance (typeof (TInterceptor), parameters);
return (TTarget) proxy;
}
private static object GetProxy (Type targetType)
{
var callexp = Expression.Call (typeof (DispatchProxy), nameof (DispatchProxy.Create), new [] {targetType, typeof (ProxyGenerator)});
return Expression.Lambda <Func <object >> (callexp) .Compile () ();
}
private void CreateInstance (Type interceptorType, object [] parameters)
{
var ctorParams = parameters.Select (x => x.GetType ()). ToArray ();
var paramsExp = parameters.Select (x => Expression.Constant (x));
var newExp = Expression.New (interceptorType.GetConstructor (ctorParams), paramsExp);
this.interceptor = Expression.Lambda <Func <IInterceptor >> (newExp) .Compile () ();
}
private void CreateInstance (IInterceptor interceptor)
{
this.interceptor = interceptor;
}
protected override object Invoke (MethodInfo method, object [] parameters)
{
return this.interceptor.Intercept (method, parameters);
}
}
How to use
class Program
{
static void Main(string[] args)
{
var poxy1 = (targetInterface)ProxyGenerator.Create(typeof(targetInterface), new SampleProxy("coreproxy1"));
poxy1.Write("here was invoked"); //---> "here was invoked by coreproxy1"
var poxy2 = (targetInterface)ProxyGenerator.Create(typeof(targetInterface), typeof(SampleProxy), "coreproxy2");
poxy2.Write("here was invoked"); //---> "here was invoked by coreproxy2"
var poxy3 = ProxyGenerator.Create<targetInterface, SampleProxy>("coreproxy3");
poxy3.Write("here was invoked"); //---> "here was invoked by coreproxy3"
}
}
public class SampleProxy : IInterceptor
{
private string proxyName { get; }
public SampleProxy(string name)
{
this.proxyName = name;
}
public object Intercept(MethodInfo method, object[] parameters)
{
Console.WriteLine(parameters[0] + " by " + proxyName);
return null;
}
}
public interface targetInterface
{
void Write(string writesome);
}
Summarize
To sum up, Microsoft's father gave us this wheel is still light and very useful.
The example code for this article can be found on my github: