English 中文(简体)
从表达式缓存编译<Func<T>>
原标题:Cache compile from Expression<Func<T>>

我有一个用于检查方法参数的类,您可以以以下形式调用该类:

public void SomeMethod(string anArg)
{
    Ensure.ArgumentNotNull(() => anArg);
}

如果参数为null,则抛出具有属性名称的ArgumentNullException。这样做:

public static void ArgumentNotNull<T>(Expression<Func<T>> expression) where T : class 
{
    var value = expression.Compile()();
    if (value == null)
    {
        throw new ArgumentNullException(expression.GetMemberName());
    }
}

其中<code>GetMemberName</code>是我编写的一个扩展方法。

我遇到的问题是,对Compile的调用非常慢,所以我想缓存结果,但我似乎无法找到一个足够唯一的缓存密钥来防止缓存冲突,但又不能唯一到使缓存无效。

到目前为止,我最大的努力是:

internal static class ExpressionCache<T>
{
    private static readonly Dictionary<string, Func<T>> Cache = new Dictionary<string, Func<T>>();

    public static Func<T> CachedCompile(Expression<Func<T>> targetSelector)
    {
        Func<T> cachedFunc;
        var cacheKey = targetSelector + targetSelector.Body.ToString();

        if (!Cache.TryGetValue(cacheKey, out cachedFunc))
        {
            cachedFunc = targetSelector.Compile();
            Cache[cacheKey] = cachedFunc;
        }

        return cachedFunc;
    }
}

但这仍然会导致缓存密钥冲突。什么可能是更好的方法?

最佳回答

这些经验来自哪里,它们是新创造的吗?如果它们被重用,您可以只使用表达式本身作为键。:

internal static class ExpressionCache<T>
{
    private static readonly Dictionary<Expression<Func<T>, Func<T>> Cache = new Dictionary<Expression<Func<T>, Func<T>>();

    public static Func<T> CachedCompile(Expression<Func<T>> targetSelector)
    {
        Func<T> cachedFunc;
        if (!Cache.TryGetValue(targetSelector, out cachedFunc))
        {
            cachedFunc = targetSelector.Compile();
            Cache[targetSelector] = cachedFunc;
        }

        return cachedFunc;
    }
}

否则,您可以窥探DLR的内部源代码http://dlr.codeplex.com/,我相信他们很好地解决了这类问题。

问题回答

而不是使用字典<;T、 V>,如果您更关心竞争条件和可读性而不是性能(我不确定这是否会是最糟糕的),您可以考虑使用ConcurrentDictionary<;T、 V>

它已经有了一个<code>GetOrAdd</code>方法,可以减少代码的编写,而且由于它与.NET 4.0一起提供,它可以确保工作并得到良好的文档记录。

var dict = new ConcurrentDictionary<Expression<Func<T>, Func<T>>();
...
var cacheKey = targetSelector; //or whatever as long as it s unique
var cachedFunc = dict.GetOrAdd(cacheKey, key => targetSelector.Compile());

此外,在比赛条件下,它可能不太容易出错。但是您必须知道GetOrAdd也不是线程安全的。如果您关心这一点,请查看CodeReview.SE上的问题,他们似乎在其中找到了解决方案。

Disclaimer: I know this is an old question that is more about forming a proper key than about a better implementation of the cache. But I think people looking for this today, might find it useful.

Jeffery Zhao有关于这个主题的一些优秀帖子,不幸的是,它们是用中文写的。一个好消息是,您可以下载ExpressionTree缓存的完整实现代码这里。我个人还有另一个建议:为什么不代码合同





相关问题
Anyone feel like passing it forward?

I m the only developer in my company, and am getting along well as an autodidact, but I know I m missing out on the education one gets from working with and having code reviewed by more senior devs. ...

NSArray s, Primitive types and Boxing Oh My!

I m pretty new to the Objective-C world and I have a long history with .net/C# so naturally I m inclined to use my C# wits. Now here s the question: I feel really inclined to create some type of ...

C# Marshal / Pinvoke CBitmap?

I cannot figure out how to marshal a C++ CBitmap to a C# Bitmap or Image class. My import looks like this: [DllImport(@"test.dll", CharSet = CharSet.Unicode)] public static extern IntPtr ...

How to Use Ghostscript DLL to convert PDF to PDF/A

How to user GhostScript DLL to convert PDF to PDF/A. I know I kind of have to call the exported function of gsdll32.dll whose name is gsapi_init_with_args, but how do i pass the right arguments? BTW, ...

Linqy no matchy

Maybe it s something I m doing wrong. I m just learning Linq because I m bored. And so far so good. I made a little program and it basically just outputs all matches (foreach) into a label control. ...