Last active
May 26, 2016 09:18
-
-
Save netcore-jroger/4ea7e769af0732956dcefc287813dd7a to your computer and use it in GitHub Desktop.
Emit动态实现接口. 并调用 Interceptor 类的 Invoke 方法。我们可以在 Invoke 方法中大做文章。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//示例 | |
using System; | |
using System.Threading.Tasks; | |
using Newtonsoft.Json; | |
using System.Net.Http; | |
using System.Collections.Generic; | |
namespace DynamicProxyBuilderInterceptor | |
{ | |
class MainClass | |
{ | |
public static void Main (string[] args) | |
{ | |
var config = new InterceptorConfiguration { | |
BaseUri = "http://localhost:9090/" | |
}; | |
var handler = new InterceptorHandler{ | |
BeforeInvoke = (methodName, arg) => { | |
Console.WriteLine ($"Invoke MethodName: {methodName}"); | |
Console.WriteLine ($"arg: {JsonConvert.SerializeObject(arg)}"); | |
}, | |
Invoke = (methodName, arg) => { | |
using (var client = new HttpClient ()) | |
{ | |
client.BaseAddress = new Uri (config.BaseUri); | |
var json = JsonConvert.SerializeObject (args); | |
var content = new FormUrlEncodedContent (new Dictionary<string, string> { | |
{ "params", json } | |
}); | |
var response = client.PostAsync ($"/api/{methodName.ToLower()}", content).Result; | |
var str = response.Content.ReadAsStringAsync().Result; | |
return str; | |
} | |
} | |
}; | |
var foo = DynamicInterfaceProxy.CreateInstance<IFoo> (handler); | |
foo.Send ("aaa"); | |
var ret1 = foo.Apply (new OrderInfo{ | |
Id = 1, | |
Code = Guid.NewGuid().ToString("D") | |
}); | |
var ret2 = foo.Find(1); | |
foo.Get ("9SDFJSDL-8F90-IOUY-KXFW-03UIS89FKSOP"); | |
Console.WriteLine ($"return value: {ret1}"); | |
Console.WriteLine ($"return value: {ret2}"); | |
Console.ReadKey (true); | |
} | |
public OrderInfo Get() | |
{ | |
return (OrderInfo)"str"; | |
} | |
} | |
public interface IFoo | |
{ | |
void Send(string message); | |
string Apply(OrderInfo order); | |
OrderInfo Find(int id); | |
[RemoteUriAttribute("foo/getorder")] | |
void Get (string code); | |
} | |
[NeedExplicitAttribute] | |
public class OrderInfo | |
{ | |
public int Id{ get; set; } | |
public string Code{get;set;} | |
public static explicit operator OrderInfo(string input) | |
{ | |
return JsonConvert.DeserializeObject<OrderInfo> (input); | |
} | |
} | |
} | |
//源代码 | |
using System; | |
using System.Collections.Generic; | |
using System.Reflection; | |
using System.Reflection.Emit; | |
using Newtonsoft.Json; | |
using System.Net.Http; | |
using System.Text; | |
using System.Threading.Tasks; | |
using System.Linq; | |
namespace DynamicProxyBuilderInterceptor | |
{ | |
public static class DynamicInterfaceProxy | |
{ | |
/// <summary> | |
/// All of the types generated for each interface. | |
/// This dictionary is indexed by the interface's type object | |
/// </summary> | |
private static Dictionary<Type, Type> _interfaceImplementations = new Dictionary<Type, Type> (); | |
private static ModuleBuilder _moduleBuilder; | |
private static AssemblyBuilder _assemblyBuilder; | |
private static readonly string _name = "ImplementInterfaceProxy"; | |
static DynamicInterfaceProxy() | |
{ | |
var an = new AssemblyName(_name){ | |
Version = new Version(1,0,0,0) | |
}; | |
_assemblyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly (an, AssemblyBuilderAccess.RunAndSave); | |
_moduleBuilder = _assemblyBuilder.DefineDynamicModule (_name, $"{_name}.dll"); | |
} | |
/// <summary> | |
/// Returns an object that implement an interface, without the need to | |
/// manually create a type that implements the interface | |
/// </summary> | |
/// <typeparam name="T">T must be an interface that is public.</typeparam> | |
/// <returns>An object that implements the T interface</returns> | |
/// <exception cref="TypeIsNotAnInterface">Thrown if T is not an interface | |
/// </exception></span> | |
public static T CreateInstance<T> (InterceptorHandler handler) where T:class | |
{ | |
Type type = typeof(T); | |
// If the type that implements the isn't created, create it | |
if (!_interfaceImplementations.ContainsKey (type)) | |
{ | |
_CreateType(type); | |
_assemblyBuilder.Save ($"{_name}.dll"); | |
} | |
// Now that the type exists to implement the interface, | |
// use the Activator to create an object | |
return (T)Activator.CreateInstance (_interfaceImplementations[type], new Interceptor(handler)); | |
} | |
private static void _CreateType (Type type) | |
{ | |
if (!type.IsInterface) | |
throw new TypeIsNotAnInterface (type); | |
TypeBuilder typeBuilder = _moduleBuilder.DefineType ($"{_name}.ImplOf{type.Name}", TypeAttributes.Class | TypeAttributes.Public); | |
typeBuilder.AddInterfaceImplementation (type); | |
// Create Constructor | |
ConstructorInfo baseConstructorInfo = typeof(object).GetConstructor (new Type[0]); | |
ConstructorBuilder constructorBuilder = typeBuilder.DefineConstructor ( | |
MethodAttributes.Public, | |
CallingConventions.Standard, | |
new Type[]{typeof(Interceptor)}); | |
ILGenerator ilGenerator = constructorBuilder.GetILGenerator (); | |
ilGenerator.Emit (OpCodes.Ldarg_0); // Load "this" | |
ilGenerator.Emit (OpCodes.Call, baseConstructorInfo); // Call the base constructor | |
//定义本地变量 | |
// 声明__Interceptor变量 | |
FieldBuilder interceptorField = typeBuilder.DefineField ("_Interceptor", typeof(Interceptor), FieldAttributes.Private); | |
ilGenerator.Emit(OpCodes.Ldarg_0); | |
ilGenerator.Emit (OpCodes.Ldarg_1); | |
ilGenerator.Emit (OpCodes.Stfld, interceptorField); | |
ilGenerator.Emit (OpCodes.Ret); // return | |
List<MethodInfo> methods = new List<MethodInfo> (); | |
_AddMethodsToList (methods, type); | |
List<PropertyInfo> properties = new List<PropertyInfo> (); | |
_AddPropertiesToList (properties, type); | |
foreach (PropertyInfo pi in properties) | |
{ | |
string piName = pi.Name; | |
Type propertyType = pi.PropertyType; | |
// Create underlying field; all properties have a field of the same type | |
FieldBuilder field = typeBuilder.DefineField ("_" + piName, propertyType, FieldAttributes.Private); | |
// If there is a getter in the interface, create a getter in the new type | |
MethodInfo getMethod = pi.GetGetMethod (); | |
if (null != getMethod) | |
{ | |
// This will prevent us from creating a default method for the property's getter | |
methods.Remove (getMethod); | |
// Now we will generate the getter method | |
MethodBuilder methodBuilder = typeBuilder.DefineMethod (getMethod.Name, | |
MethodAttributes.Public | MethodAttributes.Virtual, propertyType, | |
Type.EmptyTypes); | |
// The ILGenerator class is used to put op-codes (similar to assembly) | |
// into the method | |
ilGenerator = methodBuilder.GetILGenerator (); | |
// These are the op-codes, (similar to assembly) | |
ilGenerator.Emit (OpCodes.Ldarg_0); // Load "this" | |
// Load the property's underlying field onto the stack | |
ilGenerator.Emit (OpCodes.Ldfld, field); | |
ilGenerator.Emit (OpCodes.Ret); // Return the value on the stack | |
// We need to associate our new type's method with the | |
// getter method in the interface | |
typeBuilder.DefineMethodOverride (methodBuilder, getMethod); | |
} | |
// If there is a setter in the interface, create a setter in the new type | |
MethodInfo setMethod = pi.GetSetMethod (); | |
if (null != setMethod) { | |
// This will prevent us from creating a default method for the property's setter | |
methods.Remove (setMethod); | |
// Now we will generate the setter method | |
MethodBuilder methodBuilder = typeBuilder.DefineMethod | |
(setMethod.Name, MethodAttributes.Public | | |
MethodAttributes.Virtual, typeof(void), new Type[] { pi.PropertyType }); | |
// The ILGenerator class is used to put op-codes (similar to assembly) | |
// into the method | |
ilGenerator = methodBuilder.GetILGenerator (); | |
// These are the op-codes, (similar to assembly) | |
ilGenerator.Emit (OpCodes.Ldarg_0); // Load "this" | |
ilGenerator.Emit (OpCodes.Ldarg_1); // Load "value" onto the stack | |
// Set the field equal to the "value" on the stack | |
ilGenerator.Emit (OpCodes.Stfld, field); | |
ilGenerator.Emit (OpCodes.Ret); // Return nothing | |
// We need to associate our new type's method with the | |
// setter method in the interface | |
typeBuilder.DefineMethodOverride (methodBuilder, setMethod); | |
} | |
} | |
foreach (MethodInfo methodInfo in methods) | |
{ | |
// Get the return type and argument types | |
Type returnType = methodInfo.ReturnType; | |
List<Type> argumentTypes = new List<Type> (); | |
foreach (ParameterInfo parameterInfo in methodInfo.GetParameters()) | |
argumentTypes.Add (parameterInfo.ParameterType); | |
// Define the method | |
MethodBuilder methodBuilder = typeBuilder.DefineMethod | |
(methodInfo.Name, MethodAttributes.Public | MethodAttributes.Virtual, returnType, argumentTypes.ToArray ()); | |
// The ILGenerator class is used to put op-codes | |
// (similar to assembly) into the method | |
ilGenerator = methodBuilder.GetILGenerator (); | |
#region 调用拦截器 | |
//将 interceptorField 变量压入栈 | |
ilGenerator.Emit(OpCodes.Ldarg_0); | |
ilGenerator.Emit(OpCodes.Ldfld, interceptorField); | |
//将Call的第一个参数压栈 | |
var methodAttribute = methodInfo.GetCustomAttribute(typeof(RemoteUriAttribute)); | |
if(methodAttribute == null) | |
{ | |
ilGenerator.Emit (OpCodes.Ldstr,$"{type.Name.Substring(1).ToLower()}/{methodInfo.Name.ToLower()}"); | |
} | |
else | |
{ | |
ilGenerator.Emit (OpCodes.Ldstr, ((RemoteUriAttribute)methodAttribute).RemoteUri); | |
} | |
//将Call的第二个参数压栈 | |
Type[] argTypes = _GetParameterTypes (methodInfo); | |
LocalBuilder local = ilGenerator.DeclareLocal (typeof(Object[])); | |
ilGenerator.Emit (OpCodes.Ldc_I4, argTypes.Length); | |
ilGenerator.Emit (OpCodes.Newarr, typeof(Object)); | |
ilGenerator.Emit (OpCodes.Stloc, local); | |
ilGenerator.Emit (OpCodes.Ldloc, local); | |
for (Int32 j = 0; j < argTypes.Length; j++) | |
{ | |
ilGenerator.Emit (OpCodes.Ldc_I4, j); | |
ilGenerator.Emit (OpCodes.Ldarg, j + 1); | |
ilGenerator.Emit (OpCodes.Box, argTypes [j]); | |
ilGenerator.Emit (OpCodes.Stelem_Ref); | |
ilGenerator.Emit (OpCodes.Ldloc, local); | |
} | |
//调用 Invoke 方法 | |
ilGenerator.Emit(OpCodes.Call, typeof(Interceptor).GetMethod("Invoke", new Type[]{typeof(string), typeof(object[])})); | |
if(methodInfo.ReturnType.Equals(typeof(void))) | |
{ | |
ilGenerator.Emit(OpCodes.Pop); | |
} | |
else | |
{ | |
LocalBuilder resultLocal = ilGenerator.DeclareLocal(typeof(string)); | |
// ilGenerator.Emit(OpCodes.Unbox_Any, methodInfo.ReturnType); //此处直接使用 OpCodes.Unbox_Any 无法触发实体类中定义的显示转换方法 | |
ilGenerator.Emit(OpCodes.Stloc, resultLocal); | |
ilGenerator.Emit(OpCodes.Ldloc, resultLocal); | |
var attribute = methodInfo.ReturnType.GetCustomAttribute(typeof(NeedExplicitAttribute)); | |
if(attribute != null) | |
{ | |
ilGenerator.Emit(OpCodes.Call, methodInfo.ReturnType.GetMethod("op_Explicit")); | |
} | |
} | |
#endregion | |
ilGenerator.Emit (OpCodes.Ret); // return | |
// We need to associate our new type's method with the method in the interface | |
typeBuilder.DefineMethodOverride (methodBuilder, methodInfo); | |
} | |
Type createdType = typeBuilder.CreateType (); | |
_interfaceImplementations [type] = createdType; | |
} | |
private static void _AddMethodsToList (List<MethodInfo> methods, Type type) | |
{ | |
methods.AddRange (type.GetMethods ()); | |
foreach (Type subInterface in type.GetInterfaces()) | |
_AddMethodsToList (methods, subInterface); | |
} | |
private static void _AddPropertiesToList (List<PropertyInfo> properties, Type type) | |
{ | |
properties.AddRange (type.GetProperties ()); | |
foreach (Type subInterface in type.GetInterfaces()) | |
_AddPropertiesToList (properties, subInterface); | |
} | |
/// <summary> | |
/// 得到一个方法所有参数的Type数组,这个方法在以后会被多次调用 | |
/// </summary> | |
/// <param name="methodInfo"></param> | |
/// <returns></returns> | |
private static Type[] _GetParameterTypes (MethodInfo methodInfo) | |
{ | |
ParameterInfo[] args = methodInfo.GetParameters (); | |
Type[] argsType = new Type[args.Length]; | |
for (Int32 j = 0; j < args.Length; j++) | |
{ | |
argsType [j] = args [j].ParameterType; | |
} | |
return argsType; | |
} | |
} | |
public class InterceptorConfiguration | |
{ | |
private string _baseUri = string.Empty; | |
public string BaseUri | |
{ | |
get | |
{ | |
return this._baseUri; | |
} | |
set | |
{ | |
if (value.EndsWith ("/")) | |
{ | |
this._baseUri = value; | |
} | |
else | |
{ | |
this._baseUri = value + "/"; | |
} | |
} | |
} | |
} | |
public class InterceptorHandler | |
{ | |
public BeforeInvokeHandler BeforeInvoke { get; set; } | |
public InvokeHandler Invoke { get; set;} | |
public AfterInvokeHandler AfterInvoke{ get; set; } | |
public delegate void BeforeInvokeHandler(string methodName, params object[] args); | |
public delegate object InvokeHandler(string methodName, params object[] args); | |
public delegate void AfterInvokeHandler(string methodName, object result, params object[] args); | |
} | |
public class Interceptor | |
{ | |
private InterceptorHandler _handler; | |
public Interceptor(InterceptorHandler handler) | |
{ | |
if(handler == null) | |
throw new ArgumentNullException (nameof (handler)); | |
this._handler = handler; | |
} | |
public object Invoke(string methodName, params object[] args) | |
{ | |
if (this._handler.BeforeInvoke != null) | |
this._handler.BeforeInvoke (methodName, args); | |
object obj = null; | |
if (this._handler.Invoke != null) | |
obj = this._handler.Invoke (methodName, args); | |
return obj; | |
} | |
} | |
public class TypeIsNotAnInterface : Exception | |
{ | |
public TypeIsNotAnInterface(Type type) : base($"type {type.Name} is not an Interface") | |
{ | |
} | |
} | |
[AttributeUsage(AttributeTargets.Class)] | |
public class NeedExplicitAttribute : Attribute | |
{ | |
} | |
[AttributeUsage(AttributeTargets.Method)] | |
public class RemoteUriAttribute : Attribute | |
{ | |
public RemoteUriAttribute (string remoteUri) | |
{ | |
this.RemoteUri = remoteUri; | |
} | |
public string RemoteUri{ get; private set;} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment