以下缓存助手采用密钥名称和创建者函数。如果缓存已经包含命名对象,则返回它,否则调用创建者来创建对象,将其粘贴在缓存中并返回新对象。
public static T GetObject<T>(String name, Func<T> creator) { ... }
显然,创建者函数将被调用一次(第一次),而其他999次将被忽略。
性能影响是什么?如果这只是一个方法指针,那么我认为这将是非常简单的 - 每次只是在堆栈上按指针 - 但是如果lambda很昂贵,可能是状态的闭包 - 每次调用时实际完成了多少工作在对象已经创建之后(在栈上推送lambda只是为了被忽略)。
对此有另一种策略吗?
答案 0 :(得分:2)
让我们先给你的方法一个实现,然后我们就可以看看幕后发生了什么。
private static Dictionary<string, object> _LUT;
public static T GetObject<T>(String name, Func<T> creator)
{
object obj;
if (_LUT.TryGetValue(name, out obj))
{
return (T)obj;
}
T ret = creator();
_LUT.Add(name, ret);
return ret;
}
public string GetString(string name)
{
return GetObject<string>(name, () => "Foo");
}
GetObject(...)的说明:
IL_0000: ldsfld System.Collections.Generic.Dictionary`2[System.String,System.Object] _LUT
IL_0005: ldarg.0
IL_0006: ldloca.s System.Object (0)
IL_0008: callvirt Boolean TryGetValue(System.String, System.Object ByRef)
IL_000d: brfalse.s IL_0016
IL_000f: ldloc.0
IL_0010: unbox.any T
IL_0015: ret
IL_0016: ldarg.1
IL_0017: callvirt T Invoke()
IL_001c: stloc.1
IL_001d: ldsfld System.Collections.Generic.Dictionary`2[System.String,System.Object] _LUT
IL_0022: ldarg.0
IL_0023: ldloc.1
IL_0024: box T
IL_0029: callvirt Void Add(System.String, System.Object)
IL_002e: ldloc.1
IL_002f: ret
GetString(...)的说明:
IL_0000: ldarg.1
IL_0001: ldsfld System.Func`1[System.String] CS$<>9__CachedAnonymousMethodDelegate1
IL_0006: brtrue.s IL_0019
IL_0008: ldnull
IL_0009: ldftn System.String <GetString>b__0()
IL_000f: newobj Void .ctor(System.Object, IntPtr)
IL_0014: stsfld System.Func`1[System.String] CS$<>9__CachedAnonymousMethodDelegate1
IL_0019: ldsfld System.Func`1[System.String] CS$<>9__CachedAnonymousMethodDelegate1
IL_001e: call System.String GetObject[String](System.String, System.Func`1[System.String])
IL_0023: ret
如您所见,您的lambda存储在System.Func1[System.String]
通过引用(Why are delegates reference types?)传递给您的方法,并在需要时调用System.Func1[System.String].Invoke()
,因此方法的大小应该没有区别。
关于您是否存在替代方案的问题:
如果这是一次性功能,您可以尝试使用System.Lazy
,如果您正在寻找常规缓存支持,我建议您查看以下内容:.NET 4 Caching Support