Skip to content

Instantly share code, notes, and snippets.

@netcore-jroger
Last active May 26, 2016 09:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save netcore-jroger/4ea7e769af0732956dcefc287813dd7a to your computer and use it in GitHub Desktop.
Save netcore-jroger/4ea7e769af0732956dcefc287813dd7a to your computer and use it in GitHub Desktop.
Emit动态实现接口. 并调用 Interceptor 类的 Invoke 方法。我们可以在 Invoke 方法中大做文章。
//示例
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