CSharp - 如何从TDelegate为回调的表达式中获取参数

  显示原文与译文双语对照的内容
0 0

我试图编写一个简单的通用缓存,但是在生成足够的密钥时遇到问题,使用 System.Func 作为回调。

我想要的是能够传递一些描述的,委托,这样缓存本身就可以获得值了。 现在我正在获取异常,因为我没有传入实现或者继承from的参数。 我应该使用什么而不是 System.Func 来实现这个预期的行为?


public class SimpleCacheKeyGenerator : ICacheKey
{
 public string GetCacheKey<T>(Expression<Func<T>> action)
 {
 var body = (MethodCallExpression) action.Body;//!!! Exception Raised - action.Body is FieldExpression

 ICollection<object> parameters = (from MemberExpression expression in body.Arguments
 select
 ((FieldInfo) expression.Member).GetValue(
 ((ConstantExpression) expression.Expression).Value)).ToList();

 var sb = new StringBuilder(100);
 sb.Append(body.Type.Namespace);
 sb.Append("-");
 sb.Append(body.Method.Name);

 parameters.ToList().ForEach(x =>
 {
 sb.Append("-");
 sb.Append(x);
 });

 return sb.ToString();
 }
}

public class InMemoryCache : ICacheService
{
 private readonly ICachePolicy _cachePolicy;
 private readonly ICacheKey _cacheKey;

 public InMemoryCache(ICachePolicy cachePolicy, ICacheKey cacheKey)
 {
 _cachePolicy = cachePolicy;
 _cacheKey = cacheKey;
 }

 public T Get<T>(Func<T> getItemCallback) where T : class
 {
 var cacheID = _cacheKey.GetCacheKey(() => getItemCallback);
 var item = HttpRuntime.Cache.Get(cacheID) as T;
 if (item == null)
 {
 item = getItemCallback();

 if (_cachePolicy.RenewLeaseOnAccess)
 {
 HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, System.Web.Caching.Cache.NoAbsoluteExpiration, _cachePolicy.ExpiresAfter);
 }
 else
 {
 HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, DateTime.UtcNow + _cachePolicy.ExpiresAfter, System.Web.Caching.Cache.NoSlidingExpiration);
 }
 }

 return item;
 }
} 

时间: 原作者:

0 0

问题是,不能轻松使用表达式> 和表示同一事件的表达式,而不需要复制代码。

可以将表达式> 转换为,>. Compile() 方法,但这可以能会造成性能问题,因为编译器实际上使用了程序集发行。

下面是我在不使用表达式和编译的情况下实现相同。 你可以在标准的Linq扩展中找到相同的Pattern 。

将你的参数作为单独的对象传递。 用作参数的类型将用于委托类型推理,参数本身将提供相同类型的委托的参数。

注意,由于anonimous对象用作参数的默认ToString实现,这里实现中的缓存工作正常。


void Main()
{
 var computeCount = 0;
 var item1 = GetCached(new{x = 1, y = 2}, (arg)=>{computeCount++; return arg.x + arg.y;});
 Console.WriteLine(item1);
 var item2 = GetCached(new{x = 1, y = 2}, (arg)=>{computeCount++; return arg.x + arg.y;});
 Console.WriteLine(item2);
 var item3 = GetCached(new{x = 1, y = 3}, (arg)=>{computeCount++; return arg.x + arg.y;});
 Console.WriteLine(item3);
 Console.WriteLine("Compute count:");
 Console.WriteLine(computeCount);
}
Dictionary<string, object> _cache = new Dictionary<string, object>();
E GetCached<T, E>(T arg, Func<T,E> getter)
{
//Creating the cache key.
//Assuming T implements ToString correctly for cache to work.
 var cacheKey = arg.ToString();

 object result;

 if (!_cache.TryGetValue(cacheKey, out result))
 {
 var newItem = getter(arg);
 _cache.Add(cacheKey, newItem);
 return newItem;
 }
 else
 {
 Console.WriteLine("Cache hit: {0}", cacheKey);
 }

 return (E)result;
}

控制台输出:


3
Cache hit: { x = 1, y = 2 }
3
4
Compute count:
2

原作者:
0 0

你得到这个例外是因为 (() => getItemCallback) 意味着 (() => { return getItemCallback; })

这就是 action.Body 不是方法调用的原因,它是返回语句。 如果将代码更改为 (() => getItemCallback()),则不应该有错误。 但是你没有任何论据。

要获取基调用的参数,必须更改代码以接受表达式并编译你的lambda 。


public T Get<T>(Expression<Func<T>> getItemCallbackExpression) where T : class
{
 var cacheID = _cacheKey.GetCacheKey(getItemCallbackExpression);
 var item = HttpRuntime.Cache.Get(cacheID) as T;
 if (item == null)
 {
 item = getItemCallback.Compile()();

 if (_cachePolicy.RenewLeaseOnAccess)
 {
 HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, System.Web.Caching.Cache.NoAbsoluteExpiration, _cachePolicy.ExpiresAfter);
 }
 else
 {
 HttpContext.Current.Cache.Insert(cacheID, getItemCallback, null, DateTime.UtcNow + _cachePolicy.ExpiresAfter, System.Web.Caching.Cache.NoSlidingExpiration);
 }
 }

 return item;
}

我不推荐这种方法,因为编译表达式需要时间。

手动生成缓存密钥可能更容易和更高性能。 如果你真的想自动管理高速缓存密钥。 你可以使用 castle.Core 或者PostSharp来查看面向方面的Programmation 。 这些工具将允许你自动添加代码到一些方法中,并自动添加缓存逻辑。

原作者:
0 0

我修改了下面的代码,我得到了预期的结果,所以你可以尝试这个,我希望这会有帮助。


 public class SimpleCacheKeyGenerator
{
 public string GetCacheKey<T, TObject>(Expression<Func<T, TObject>> action)
 {
 var body = (MethodCallExpression) action.Body;
 ICollection<object> parameters = body.Arguments.Select(x => ((ConstantExpression) x).Value).ToList();

 var sb = new StringBuilder(100);
 sb.Append(body.Type.Namespace);
 sb.Append("-");
 sb.Append(body.Method.Name);

 parameters.ToList().ForEach(x =>
 {
 sb.Append("-");
 sb.Append(x);
 });

 return sb.ToString();
 }
}

public class InMemoryCache
{
 public void Get<T, TObject>(Expression<Func<T, TObject>> getItemCallback)
 {
 var generator = new SimpleCacheKeyGenerator();
 Console.WriteLine(generator.GetCacheKey(getItemCallback));
 }
}

主要:


private static void Main(string[] args)
 {
 var cache = new InMemoryCache();

 var tt = new SomeContextImpl();
 cache.Get<SomeContextImpl, string>(x => x.Any("hello","hi"));

 Console.ReadKey();
 }

somcontextimpl:


 public class SomeContextImpl
{
 public string Any(string parameter1, string parameter2) { return""; }
}

...