Это не способ сделать это.Прямо сейчас ваше поле _method
является делегатом типа Func<T>
, и вы ожидаете, что его body содержит еще один метод, который фактически выполняется.Этого можно ожидать от ваших абонентов.Я бы забыл об этом подходе и искал бы что-то другое.
Один из способов - предоставить метод, который принимает массив объектов в качестве параметра (Func<object[], T>
), а затем вызывать его напрямую с соответствующими параметрами (но никогда не метод в своем теле).Даже это не так часто встречается для строго типизированного языка, такого как C #, так как вы теряете безопасность всех типов (но опять же, вы действительно хотите быть достаточно гибкими с этой конструкцией, которую вы разрабатываете).получить экземпляр MethodInfo
, а затем использовать его метод Invoke
.В некотором смысле, это может даже лучше выразить ваши намерения, потому что будет очевидно, что исполняемый метод способен на что угодно.
Далее, вы можете использовать обобщенные элементы для обеспечения некоторой безопасности типов и требовать, чтобы все вашивходные параметры заключены в один класс параметров.В этом случае у вас может быть строго типизированный метод Func<Tparam, Tresult>
, и ваш метод Execute
примет экземпляр Tparam
в качестве параметра.Это исключило бы необходимость в любом отражении.
[Редактировать]
Как я уже писал, я постараюсь избежать отражения.Поскольку вы написали, что вам в основном нужен кэш результатов метода, простой подход может выглядеть примерно так:
Создайте оболочку для вашего списка параметров, чтобы вы могли сравнивать их «по значению»,Я добавил пример класса, но вы можете даже разрешить явно передавать IEqualityComparer
, чтобы вам не приходилось переопределять Equals
для каждого частичного параметра.
// implements `IEquatable` for a list of parameters
class Parameters : IEquatable<Parameters>
{
private readonly object[] _parameters;
public Parameters(object[] parms)
{
_parameters = parms;
}
#region IEquatable<Parameters> Members
public bool Equals(Parameters other)
{
if (other == null)
return false;
if (_parameters.Length != other._parameters.Length)
return false;
// check each parameter to see if it's equal
// ...
}
public override bool Equals(object obj)
{
return Equals(obj as Parameters);
}
public override int GetHashCode()
{ ... }
#endregion
}
Создать кеш для одного сервиса.Используя описанный выше класс-оболочку, он должен просто проверить, существует ли кэшированный результат:
// contains cached results for a single service
class CachedCallInfo
{
private readonly Func<object[], object> _method;
private readonly Dictionary<Parameters, object> _cache
= new Dictionary<Parameters, object>();
public CachedCallInfo(Func<object[], object> method)
{
_method = method;
}
public T GetResult<T>(params object[] parameters)
{
// use out Parameters class to ensure comparison
// by value
var key = new Parameters(parameters);
object result = null;
// result exists?
if (!_cache.TryGetValue(key, out result))
{
// do the actual service call
result = _method(parameters);
// add to cache
_cache.Add(key, result);
}
return (T)result;
}
}
Создать последний класс, который будет ссылаться на службы по имени:
public class ServiceCache
{
private readonly Dictionary<string, CachedCallInfo> _services =
new Dictionary<string, CachedCallInfo>();
public void RegisterService(string name, Func<object[], object> method)
{
_services[name] = new CachedCallInfo(method);
}
// "params" keyword is used to simplify method calls
public T GetResult<T>(string serviceName, params object[] parameters)
{
return _services[serviceName].GetResult<T>(parameters);
}
}
Тогда настройки вашего кэша будут выглядеть так:
serviceCache.RegisterService("ServiceA", @params => DoSomething(@params));
serviceCache.RegisterService("ServiceB", @params => SomethingElse(@params));
И вы бы просто назвали это так:
var result = serviceCache.GetResult("ServiceA", paramA, paramB, paramC);