using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Text; namespace ZeroLevel.Services.Invokation { /// /// Обертка для вызова методов /// public class InvokeWrapper : IInvokeWrapper { /// /// Кэш делегатов /// protected readonly Dictionary _invokeCachee = new Dictionary(); #region Static helpers /// /// Создает скомпилированное выражение для быстрого вызова метода, возвращает идентификатор выражения и делегат для вызова /// /// Оборачиваемый метод /// Кортеж с идентификатором выражения и делегатом protected static Tuple CreateCompiledExpression(MethodInfo method) { var targetArg = Expression.Parameter(typeof(object)); // Цель на которой происходит вызов var argsArg = Expression.Parameter(typeof(object[])); // Аргументы метода var parameters = method.GetParameters(); Expression body = Expression.Call( method.IsStatic ? null : Expression.Convert(targetArg, method.DeclaringType), // тип в котором объявлен метод method, parameters.Select((p, i) => Expression.Convert(Expression.ArrayIndex(argsArg, Expression.Constant(i)), p.ParameterType))); if (body.Type == typeof(void)) body = Expression.Block(body, Expression.Constant(null)); else if (body.Type.IsValueType) body = Expression.Convert(body, typeof(object)); var identity = CreateMethodIdentity(method.Name, parameters.Select(p => p.ParameterType).ToArray()); return new Tuple(identity.ToString(), Expression.Lambda(body, targetArg, argsArg).Compile()); } /// /// Оборачивает вызов делегата /// /// Оборачиваемый делегат /// Кортеж с идентификатором выражения и делегатом protected static Tuple CreateCompiledExpression(Delegate handler) { return CreateCompiledExpression(handler.GetMethodInfo()); } #endregion #region Helpers /// /// Идентификатр однозначно определяющий метод на уровне типа (но не на глобальном уровне) /// /// Имя метода /// Типы аргументов метода /// internal static string CreateMethodIdentity(string name, params Type[] argsTypes) { var identity = new StringBuilder(name); identity.Append("("); if (null != argsTypes) { for (var i = 0; i < argsTypes.Length; i++) { identity.Append(argsTypes[i].Name); if ((i + 1) < argsTypes.Length) identity.Append("."); } } identity.Append(")"); return identity.ToString(); } #endregion #region Configure by Type public IEnumerable Configure() { return Configure(typeof(T)); } public IEnumerable Configure(string methodName) { return Configure(typeof(T), methodName); } public IEnumerable Configure(Func filter) { return Configure(typeof(T), filter); } public IEnumerable Configure(Type type) { var result = type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)?.Select(CreateCompiledExpression); Configure(result); return result.Select(r => r.Item1).ToList(); } public IEnumerable Configure(Type type, string methodName) { var result = type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)?.Where(m => m.Name.Equals(methodName)) ?.Select(CreateCompiledExpression); Configure(result); return result.Select(r => r.Item1).ToList(); } public IEnumerable ConfigureGeneric(Type type, string methodName) { var result = type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)?.Where(m => m.Name.Equals(methodName)) ?.Select(method => method.MakeGenericMethod(typeof(T))).Select(CreateCompiledExpression); Configure(result); return result.Select(r => r.Item1).ToList(); } public IEnumerable ConfigureGeneric(Type instanceType, Type genericType, string methodName) { var result = instanceType.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)?.Where(m => m.Name.Equals(methodName)) ?.Select(method => method.MakeGenericMethod(genericType)).Select(CreateCompiledExpression); Configure(result); return result.Select(r => r.Item1).ToList(); } public IEnumerable ConfigureGeneric(Type type, Func filter) { var result = type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)?.Where(filter) ?.Select(method => method.MakeGenericMethod(typeof(T))).Select(CreateCompiledExpression); Configure(result); return result.Select(r => r.Item1).ToList(); } public IEnumerable ConfigureGeneric(Type instanceType, Type genericType, Func filter) { var result = instanceType.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)?.Where(filter) ?.Select(method => method.MakeGenericMethod(genericType)).Select(CreateCompiledExpression); Configure(result); return result.Select(r => r.Item1).ToList(); } public IEnumerable Configure(Type type, Func filter) { var result = type.GetMethods(BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.FlattenHierarchy)?.Where(filter) ?.Select(CreateCompiledExpression); Configure(result); return result.Select(r => r.Item1).ToList(); } #endregion #region Configure by MethodInfo /// /// Вносит в кэш вызов указанного метода /// /// Метод /// Идентификатор для вызова public string Configure(MethodInfo method) { var invoke = CreateCompiledExpression(method); Configure(invoke); return invoke.Item1; } /// /// Вносит в кэш вызов указанного делегата /// /// Делегат /// Идентификатор вызова public string Configure(Delegate handler) { var invoke = CreateCompiledExpression(handler); Configure(invoke); return invoke.Item1; } public IEnumerable Configure(IEnumerable list) { var result = list?.Select(CreateCompiledExpression); if (null != result) { Configure(result); return result.Select(r => r.Item1).ToList(); } return Enumerable.Empty(); } #endregion #region Configuration /// /// Наполнение кэша из списка методов с идентификаторами /// protected void Configure(IEnumerable> list) { foreach (var invoke in list) { Configure(invoke); } } /// /// Добавление вызова в кэш /// protected void Configure(Tuple invoke) { _invokeCachee[invoke.Item1] = invoke.Item2; } #endregion #region Invoking /// /// Вызов статического метода по идентификатору, в случае отсутствия метода в кеше будет брошено исключение KeyNotFoundException /// /// Идентификатор метода /// Аргументы метода /// Результат выполнения public object InvokeStatic(string identity, object[] args) { if (_invokeCachee.ContainsKey(identity)) { return _invokeCachee[identity](null, args); } throw new KeyNotFoundException(String.Format("Not found method with identity '{0}'", identity)); } /// /// Вызов метода по идентификатору, в случае отсутствия метода в кеше будет брошено исключение KeyNotFoundException /// /// Инстанс на котором вызывается метод /// Идентификатор метода /// Аргументы метода /// Результат выполнения public object Invoke(object target, string identity, object[] args) { if (_invokeCachee.ContainsKey(identity)) { return _invokeCachee[identity](target, args); } throw new KeyNotFoundException(String.Format("Not found method with identity '{0}'", identity)); } public object Invoke(object target, string identity) { if (_invokeCachee.ContainsKey(identity)) { return _invokeCachee[identity](target, null); } throw new KeyNotFoundException(String.Format("Not found method with identity '{0}'", identity)); } /// /// Выполнение статического закэшированного метода /// /// Имя метода /// Аргументы метода /// /// Результат выполнения public object Invoke(string methodName, object[] args) { return InvokeStatic(CreateMethodIdentity(methodName, args.Select(a => a.GetType()).ToArray()), args); } #endregion #region Helpers /// /// Запрос идентификатора для метода /// /// Имя метода /// Список типов аргументов метода /// Идентификатор public string GetInvokerIdentity(string methodName, params Type[] argsTypes) { return CreateMethodIdentity(methodName, argsTypes); } /// /// Запрос делегата оборачивающего метод /// /// Идентификатор метода /// Делегат public Invoker GetInvoker(string identity) { if (_invokeCachee.ContainsKey(identity)) { return _invokeCachee[identity]; } return null; } /// /// Запрос делегата оборачивающего метод /// /// Имя метода /// Список типов аргументов метода /// Делегат public Invoker GetInvoker(string methodName, params Type[] argsTypes) { return GetInvoker(CreateMethodIdentity(methodName, argsTypes)); } #endregion #region Factories public static IInvokeWrapper Create() { return new InvokeWrapper(); } #endregion } }