You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Zero/ZeroLevel/Services/Invokation/InvokeWrapper.cs

362 lines
15 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
namespace ZeroLevel.Services.Invokation
{
/// <summary>
/// Обертка для вызова методов
/// </summary>
public class InvokeWrapper : IInvokeWrapper
{
/// <summary>
/// Кэш делегатов
/// </summary>
protected readonly Dictionary<string, Invoker> _invokeCachee = new Dictionary<string, Invoker>();
#region Static helpers
/// <summary>
/// Создает скомпилированное выражение для быстрого вызова метода, возвращает идентификатор выражения и делегат для вызова
/// </summary>
/// <param name="method">Оборачиваемый метод</param>
/// <returns>Кортеж с идентификатором выражения и делегатом</returns>
protected static Tuple<string, Invoker> 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<string, Invoker>(identity.ToString(),
Expression.Lambda<Invoker>(body, targetArg, argsArg).Compile());
}
/// <summary>
/// Оборачивает вызов делегата
/// </summary>
/// <param name="handler">Оборачиваемый делегат</param>
/// <returns>Кортеж с идентификатором выражения и делегатом</returns>
protected static Tuple<string, Invoker> CreateCompiledExpression(Delegate handler)
{
return CreateCompiledExpression(handler.GetMethodInfo());
}
#endregion
#region Helpers
/// <summary>
/// Идентификатр однозначно определяющий метод на уровне типа (но не на глобальном уровне)
/// </summary>
/// <param name="name">Имя метода</param>
/// <param name="argsTypes">Типы аргументов метода</param>
/// <returns></returns>
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<string> Configure<T>()
{
return Configure(typeof(T));
}
public IEnumerable<string> Configure<T>(string methodName)
{
return Configure(typeof(T), methodName);
}
public IEnumerable<string> Configure<T>(Func<MethodInfo, bool> filter)
{
return Configure(typeof(T), filter);
}
public IEnumerable<string> 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<string> 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<string> ConfigureGeneric<T>(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<string> 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<string> ConfigureGeneric<T>(Type type, Func<MethodInfo, bool> 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<string> ConfigureGeneric(Type instanceType, Type genericType, Func<MethodInfo, bool> 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<string> Configure(Type type, Func<MethodInfo, bool> 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
/// <summary>
/// Вносит в кэш вызов указанного метода
/// </summary>
/// <param name="method">Метод</param>
/// <returns>Идентификатор для вызова</returns>
public string Configure(MethodInfo method)
{
var invoke = CreateCompiledExpression(method);
Configure(invoke);
return invoke.Item1;
}
/// <summary>
/// Вносит в кэш вызов указанного делегата
/// </summary>
/// <param name="handler">Делегат</param>
/// <returns>Идентификатор вызова</returns>
public string Configure(Delegate handler)
{
var invoke = CreateCompiledExpression(handler);
Configure(invoke);
return invoke.Item1;
}
public IEnumerable<string> Configure(IEnumerable<MethodInfo> list)
{
var result = list?.Select(CreateCompiledExpression);
if (null != result)
{
Configure(result);
return result.Select(r => r.Item1).ToList();
}
return Enumerable.Empty<string>();
}
#endregion
#region Configuration
/// <summary>
/// Наполнение кэша из списка методов с идентификаторами
/// </summary>
protected void Configure(IEnumerable<Tuple<string, Invoker>> list)
{
foreach (var invoke in list)
{
Configure(invoke);
}
}
/// <summary>
/// Добавление вызова в кэш
/// </summary>
protected void Configure(Tuple<string, Invoker> invoke)
{
_invokeCachee[invoke.Item1] = invoke.Item2;
}
#endregion
#region Invoking
/// <summary>
/// Вызов статического метода по идентификатору, в случае отсутствия метода в кеше будет брошено исключение KeyNotFoundException
/// </summary>
/// <param name="identity">Идентификатор метода</param>
/// <param name="args">Аргументы метода</param>
/// <returns>Результат выполнения</returns>
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));
}
/// <summary>
/// Вызов метода по идентификатору, в случае отсутствия метода в кеше будет брошено исключение KeyNotFoundException
/// </summary>
/// <param name="target">Инстанс на котором вызывается метод</param>
/// <param name="identity">Идентификатор метода</param>
/// <param name="args">Аргументы метода</param>
/// <returns>Результат выполнения</returns>
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));
}
/// <summary>
/// Выполнение статического закэшированного метода
/// </summary>
/// <param name="methodName">Имя метода</param>
/// <param name="args">Аргументы метода</param>
/// /// <returns>Результат выполнения</returns>
public object Invoke(string methodName, object[] args)
{
return InvokeStatic(CreateMethodIdentity(methodName, args.Select(a => a.GetType()).ToArray()), args);
}
#endregion
#region Helpers
/// <summary>
/// Запрос идентификатора для метода
/// </summary>
/// <param name="methodName">Имя метода</param>
/// <param name="argsTypes">Список типов аргументов метода</param>
/// <returns>Идентификатор</returns>
public string GetInvokerIdentity(string methodName, params Type[] argsTypes)
{
return CreateMethodIdentity(methodName, argsTypes);
}
/// <summary>
/// Запрос делегата оборачивающего метод
/// </summary>
/// <param name="identity">Идентификатор метода</param>
/// <returns>Делегат</returns>
public Invoker GetInvoker(string identity)
{
if (_invokeCachee.ContainsKey(identity))
{
return _invokeCachee[identity];
}
return null;
}
/// <summary>
/// Запрос делегата оборачивающего метод
/// </summary>
/// <param name="methodName">Имя метода</param>
/// <param name="argsTypes">Список типов аргументов метода</param>
/// <returns>Делегат</returns>
public Invoker GetInvoker(string methodName, params Type[] argsTypes)
{
return GetInvoker(CreateMethodIdentity(methodName, argsTypes));
}
#endregion
#region Factories
public static IInvokeWrapper Create()
{
return new InvokeWrapper();
}
#endregion
}
}

Powered by TurnKey Linux.