背景
所以前提是我有一个用属性提供数据的类。
class ExampleClass
{
string _largeCalculatedVariable = null;
public string largeCalculatedVariable
{
get
{
if (_largeCalculatedVariable == null)
{
_largeCalculatedVariable = LongRunningCalculate();
}
return _largeCalculatedVariable
}
}
}
该属性隐藏了这样一个事实:如果数据尚未生成,则数据可以即时计算数据但如果之前已经预先计算或计算过,则会返回缓存值。
问题
问题是可能会生成一大堆ExampleClass,如果我访问largeCalculatedVariable
就足够了,我可能会耗尽内存。既然我可以随时重新计算该值,是否可以告诉.NET在需要内存时放弃_largeCalculatedVariable
?
注意:我觉得我可能在这里使用了错误的设计模式。
答案 0 :(得分:7)
如果您使用的是.NET 4.0或更新版本,您可以使用MemoryCache
类,您可以将其设置为有应该存储多少数据的规则,如果它已经命中,它将自行清除以腾出空间这是限制。
class ExampleClass
{
//This is a static constructor;
static ExampleClass()
{
var settings = new NameValueCollection();
settings.Add("PhysicalMemoryLimitPercentage", "75");
_cache = new MemoryCache("ExampleClassCache", settings);
}
private static MemoryCache _cache;
public ExampleClass()
{
_cacheKey = Guid.NewGuid().ToString();
}
private readonly string _cacheKey;
public string largeCalculatedVariable
{
get
{
var record = _cache.Get(_cacheKey) as string;
//If record was null that means the item was not in the cache.
if(record == null)
{
record = LongRunningCalculate();
_cache.Add(_cacheKey, record, new CacheItemPolicy(), null);
}
return record;
}
}
}
如果你想要,你也可以让ExampleClass
从缓存中取出物品,以便在处理对象时释放空间,实现起来并不多。
class ExampleClass : IDisposable
{
//...Everything else is the same from the previous code example.
public void Dispose()
{
Dispose(true)
GC.SupressFinialize(this);
}
bool _disposed;
protected virtual void Dispose(bool disposing)
{
if(!_disposed)
{
if(disposing)
{
//Nothing to do here, we want to remove from the cache if Dispose or the finalizer is called.
//But you may have IDisposeable objects in your real class, they should be disposed in here.
}
if(_cacheKey != null)
_cache.Remove(_cacheKey, null);
_disposed = true;
}
}
~ExampleClass()
{
Dispose(false);
}
}
答案 1 :(得分:3)
您可以使用WeakReference
。它允许保留对某些数据的引用,但告诉Garbage Collector
在需要时声明该引用,这正是您想要的。
弱引用允许垃圾收集器在仍允许应用程序访问对象的同时收集对象。如果您需要该对象,您仍然可以获得它的强引用并防止它被收集。
_data = new WeakReference("Hamster");
return _data.IsAlive ? data.Target : CreateData();
答案 2 :(得分:1)
我想补充一点,除了使用 WeakReference 和 MemoryCache 的优秀答案(后者可能是您更好的选择)使用类型Lazy<>
。
此类型已经解决了您可能遇到的任何并发问题。
有人可能会想:'我没有多线程,我为什么要关心?'
Lazy<>
从.Net 4.0开始我总是实现像这样的延迟加载属性
string Lazy<string> _largeCalculatedVariable = null;//No need to set it to null but whatever you prefer
public string largeCalculatedVariable
{
get
{
if (_largeCalculatedVariable == null)
{
_largeCalculatedVariable = new Lazy<string>(()=>
{
_largeCalculatedVariable = LongRunningCalculate();
}
}
return _largeCalculatedVariable.Value
}
}