通过lambda表达式创建的委托是否可以保证缓存?

时间:2013-07-31 12:09:18

标签: c# caching lambda delegates

它似乎正在以这种方式工作,但它是在规范中的某个地方说明还是只是我不能真正依赖的实现细节?我正在尝试通过仅创建一次表达式树并缓存它来加快属性/字段名称提取。我通过将树包装在lambda中并将其用作缓存的键来实现此目的。如果运行时决定在每次命中相同的lambda表达式时创建一个新委托,它就会崩溃。

// KeyValuePair<string, T> GetPair<T>(Func<Expression<Func<T>>> val)...
var item = new Item { Num = 42 };
var pair = GetPair(() => () => item.Num); // guaranteed to be the same instance?
// pair.Key = "Num"
// pair.Value = 42

修改 好的,这是完整的事情。它似乎有效,并且似乎不会在此过程中产生任何垃圾。

另一个编辑: 好的,改变它,这似乎没有捕获任何东西,它的工作速度更快!

using System;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;

class Program
{
    static void Main(string[] args) {
        var pair = new Pair<int>();
        var pair2 = new Pair<string>();
        var item = new Item { Num = 42, Word = "Answer" };
        double ratio = 1;
        var sw = Stopwatch.StartNew();
        for (int i = 0;; i++) {
            if ((i & 0xFFF) == 0 && sw.ElapsedMilliseconds > 2000) {
                Console.WriteLine("literal: {0:N0}", i);
                ratio *= i;
                break;
            }
            Assign(pair, "Num", item.Num); Assign(pair2, "Word", item.Word);
            Assign(pair, "Num", item.Num); Assign(pair2, "Word", item.Word);
            Assign(pair, "Num", item.Num); Assign(pair2, "Word", item.Word);
            Assign(pair, "Num", item.Num); Assign(pair2, "Word", item.Word);
        }
        sw = Stopwatch.StartNew();
        for (int i = 0; ; i++) {
            if ((i & 0xFFF) == 0 && sw.ElapsedMilliseconds > 2000) {
                item = new Item { Num = 42, Word = "Answer" };
                Console.WriteLine("expression: {0:N0}", i);
                ratio /= i;
                break;
            }
            Assign4(pair, item, () => it => it.Num); Assign4(pair2, item, () => it => it.Word);
            Assign4(pair, item, () => it => it.Num); Assign4(pair2, item, () => it => it.Word);
            Assign4(pair, item, () => it => it.Num); Assign4(pair2, item, () => it => it.Word);
            Assign4(pair, item, () => it => it.Num); Assign4(pair2, item, () => it => it.Word);

        }
        Console.WriteLine(ratio.ToString("F3"));
        Console.ReadLine();
    }

    static void Assign<T>(Pair<T> pair, string name, T value) {
        pair.Name = name;
        pair.Value = value;
    }

    static void Assign4<T, U>(Pair<T> pair, U item, Func<Expression<Func<U, T>>> value,
        [CallerFilePath]string path = "", [CallerLineNumber]int line = 0) {

        int key = ((path.Length << 20) + line) % Cache<U, T>.Length;
//        int key = value.GetHashCode() % Cache<T>.Length;
        while (true) {
            var bucket = Cache<U, T>.Records[key];
            if (bucket.Literal == null) break;
            if (object.ReferenceEquals(bucket.Literal, value)) {
                pair.Name = bucket.FieldName;
                pair.Value = bucket.Getter(item);
                return;
            }
            key += 1;
            if (key == Cache<U, T>.Length) key = 0;
        }
        var tree = value();
        var getter = tree.Compile();
        string name = (tree.Body as MemberExpression).Member.Name;
        Cache<U, T>.Records[key] = new Cache<U, T>.Record {
            Literal = value,
            FieldName = name,
            Getter = getter,
        };
        pair.Name = name;
        pair.Value = getter(item);
    }
}

class Cache<U, T>
{
    public struct Record
    {
        public Func<Expression<Func<U, T>>> Literal;
        public string FieldName;
        public Func<U, T> Getter;
    }
    public const int Length = 997;
    public static Record[] Records = new Record[Length];
}

class Pair<T>
{
    public string Name;
    public T Value;
}

class Item
{
    public int Num;
    public string Word;
}

1 个答案:

答案 0 :(得分:4)

在这种情况下不能是同一个实例 - 每次执行这对行时,捕获的变量(item)都会有所不同。

即使 可以是同一个实例,也无法保证。根据我记得的MS C#编译器,不捕获任何变量(甚至不是this)的lambda表达式将被缓存在静态变量中 - 但我不确定是什么别的是。