[英]Are delegates created via lambda expressions guaranteed to be cached?
It seems to be working that way, but is it stated somewhere in the spec or just an implementation detail I can't really depend on? 它似乎正在以这种方式工作,但是它是在规范中某个地方说明,还是只是我不能真正依靠的实现细节? I'm trying to speed up property/field name extraction faster by creating the expression tree only once and caching it.
我试图通过仅创建一次表达式树并将其缓存来加快属性/字段名称提取的速度。 I do this by wrapping the tree in a lambda and using it as a key for the cache.
为此,我将树包装在lambda中并将其用作缓存的键。 And it will break down miserably if the runtime decides to create a new delegate every time it hits the same lambda expression.
如果运行时每次遇到相同的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
Edit: Ok, here is the full thing. 编辑:好的,这是全部。 It seems it works and doesn't seem to generate any garbage in the process.
似乎可行,并且在此过程中似乎不会产生任何垃圾。
Another Edit: Ok, changed it, this doesn't seem to capture anything, and it works even faster! 另一个编辑:好的,对其进行了更改,它似乎无法捕获任何内容,并且运行速度甚至更快!
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;
}
It can't be the same instance in this case - the captured variable ( item
) will be different each time you execute this pair of lines. 在这种情况下,它不能是相同的实例-每次执行这对行时,捕获的变量(
item
)都会不同。
Even where it can be the same instance, it isn't guaranteed. 即使可以是同一实例,也无法保证。 From what I remember of the MS C# compiler, lambda expressions which don't capture any variables (not even
this
) will be cached in static variables - but I'm not sure that anything else is. 从我记得的MS C#编译器的角度来看,不会捕获任何变量(甚至不是
this
)的lambda表达式将被缓存在静态变量中-但我不确定是否还有其他东西。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.