我暂时写了这个方法:
public static Func<T> WeakCacheFor<T>( Func<T> provider ) where T: class
{
var cache = new WeakReference(null);
return () => {
var x = (T)cache.Target;
if( x == null )
{
x = provider();
cache.Target = x;
}
return x;
};
}
所以有点背景知识:
我有一些冗长的传统方法,看起来有点像这样:
var id = GetDatabaseId();
if(a){
var data = GetLoader().Init(id).GetData(); // expensive!
// do stuff with data
}
if(b){
// don't load data
}
... lots more variations, some contain GetLoader().Init(id).GetData(); some don't....
我的潜在解决方案是:
var id = GetDatabaseId();
var loadData = WeakCacheFor(() => GetLoader().Init(id).GetData());
if(a){
var data = loadData();
// do stuff with data
}
if(b){
// don't load data
}
... lots more variations, some contain loadData(); some don't....
我对此的看法:
我的问题:
WeakCacheFor
方法中遗漏了哪些可能导致无意中被强烈引用的内容?我怀疑自己可能过于聪明,但即使我是这样,在其他任何人看来这样的解决方案是否可以在其他情况下有用?
更新:修改了函数,因为apparently you can't trust .IsAlive
更新:我意识到返回的Func
将在方法结束时超出范围,因此我根本不需要弱参数,而正常的参考将正常工作。我觉得我遇到了“看不见森林为树木”的案例。
答案 0 :(得分:5)
我没有看到使用弱引用的任何意义。一旦你加载了数据,几乎没有理由把它扔掉,直到你确定它不再有用为止。
您正在实施的是延迟加载模式的变体。坚持简单的模式,只需使用项目的常规参考:
public static Func<T> LazyLoad<T>(Func<T> provider) where T : class {
T item = null;
return () => {
if (item == null) {
item = provider();
}
return item;
};
}
(还有一个小提示:你过分使用var
关键字。)
答案 1 :(得分:2)
一般性意见:
对于以下讨论,请考虑:
foo = WeakCacheFor<Foo>(() => CreateFoo());
案例#1:将foo用作长生命变量(例如长生命类的成员或全局变量)
你的解决方案在这里。变量将在需要时创建,并在系统在GC期间释放资源时销毁。
但请注意,如果foo时间昂贵但内存便宜,那么它可能会使用单例模式而是在应用程序的持续时间内加载一次?
案例#2。使用foo作为局部变量。
在这种情况下,我猜最好使用单例模式。考虑这样的例子:
static void Main(string[] args)
{
MethodA(5, 7);
MethodA(8, 9);
}
static void MethodA(int a, int b)
{
var foo = WeakCacheFor<Foo>(() => new Foo());
if (a > 3)
{
Use(foo);
if (a * b == 35)
{
GC.Collect(); // Simulate GC
Use(foo);
}
else if(b % 6 == 2)
{
Use(foo);
}
}
}
foo将被创建3次。如果您对“模拟GC”行进行评论,则需要2次。你也不能将它用于IDisposable classess。
现在让我们尝试一个单身人士:
static void MethodA(int a, int b)
{
using (var foo = new MySingleton<Foo>(() => new Foo()))
{
if (a > 3)
{
Use(foo);
if (a * b == 35)
{
GC.Collect(); // Simulate GC
Use(foo);
}
else if (b % 6 == 2)
{
Use(foo);
}
}
}
}
正如您所看到的,代码几乎没有改变,但现在我们只有2次调用foo ctor和IDisposable支持。
粗略的单例实现:
class MySingleton<T> : IDisposable
where T : class
{
private T _value;
private Func<T> _provider;
public MySingleton(Func<T> provider)
{
_provider = provider;
}
public T Get()
{
if (_value == null)
{
_value = _provider();
}
return _value;
}
#region IDisposable Members
public void Dispose()
{
if(_value == null)
return;
IDisposable disposable = _value as IDisposable;
if(disposable != null)
disposable.Dispose();
}
#endregion
}
其余的代码:
class Foo : IDisposable
{
public void Dispose() {}
}
static void Use(MySingleton<Foo> foo)
{
foo.Get();
}
static void Use(Func<Foo> foo)
{
foo();
}