0

我暂时写了这个方法:

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....

我对此的看法:

  • 超出这个方法调用的范围我不需要缓存,所以只要方法一返回GC就收集就好了
  • 如果代码采用不需要加载数据的路径,则不会发生命中
  • 如果它确实需要数据,如果再次需要,它将被缓存在弱引用中。
  • 如果 GC 确实在中途收集,则无关紧要,因为它只会重新加载。

我的问题:

  1. 这真的会奏效吗?- 我在WeakCacheFor方法中是否遗漏了任何可能导致无意中被强烈引用的内容?
  2. 我是不是为了自己的利益太聪明了?- 即使不需要,我是否应该只招致命中并将数据缓存在普通的局部变量中?

我怀疑我可能太聪明了,但即使我是,这在其他人看来是否像一个可以在其他情况下有用的解决方案?

更新:修改功能,因为显然你不能信任 .IsAlive

更新:我意识到返回的Func将在方法结束时超出范围,所以我根本不需要弱引用,普通的引用就可以了。我认为我正遭受“只见树木不见森林”的情况。

4

2 回答 2

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关键字方式太多了。)

于 2009-04-02T02:07:40.610 回答
2

几个一般性评论:

  1. 您的解决方案(以及我的)不是线程安全的。
  2. 它不是为使用 IDisposable 对象而设计的。

对于以下讨论,请考虑:

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 类。

现在让我们尝试一个单例:

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();
}
于 2009-04-02T02:22:06.333 回答