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
}
}