实现 对象在内存中的引用一致性 之第一步
-
原委
废话不要,他们都该删。
虽然写了个 基于数据库的代码生成器 与大家分享,但并不擅长数据库开发;相反,面向对象才是我的爱。
多年的酝酿让我收获良多,直觉这是个令人激动的开发模式。或许是因为高中学历吧,我的思想还是朴素的:在实施软件开发时,第一反应是对象而不是数据库。
-
实施
朴素的思想让人选择朴素的视角,一种朴素的视角看到的是:
Assert.AreSame(employee1.Department, employee2.Department);
或许更适用些的断言是:
if (model1.Id == model2.Id) Assert.AreSame(model1, model2);
实现此断言即是实现 对象在内存中的唯一性,不过我还是觉得叫 对象在内存中的引用一致性 贴切点;
开始实现吧。假设我们已经分析过业务了,那接下来的第一步当然是业务模型的建立了,这一步很是顺其自然且乏味地就能 下载到结果:
紧接上一步,我们发现了一个显眼的问题:大值属性。
这真是个好问题,让人莫名地开心起来。这个问题的解决就是我们第二步需要做的了,不过还是妙手偶得之地就能 下载到结果:
实现的要点在这里:
using System; using System.Runtime.Caching; namespace Codesir.Net.Identical { /// <summary> /// 实现软引用,同时具备 缓存的长时效 和 弱引用的一致性 优点。 /// </summary> public class CachedWeakReference<T> where T : class { static MemoryCache DefaultMemoryCache = MemoryCache.Default; static CacheItemPolicy DefaultCacheItemPolicy = new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(20) }; WeakReference weakReference; MemoryCache memoryCache; CacheItemPolicy cacheItemPolicy; string cacheKey; /// <summary> /// 使用指定的对象初始化一个软引用实例。 /// </summary> /// <param name="target">要缓存并跟踪的对象。</param> public CachedWeakReference(T target) : this(target, CachedWeakReference<T>.DefaultMemoryCache, CachedWeakReference<T>.DefaultCacheItemPolicy) { } /// <summary> /// 使用指定的对象和缓存选项初始化一个软引用实例。 /// </summary> /// <param name="target">要缓存并跟踪的对象。</param> /// <param name="memoryCache">要跟踪的对象加入到的内存缓存。</param> /// <param name="cacheItemPolicy">要跟踪的对象的缓存过期策略。</param> public CachedWeakReference(T target, MemoryCache memoryCache, CacheItemPolicy cacheItemPolicy) { if (target == null) throw new ArgumentNullException("target"); if (memoryCache == null) throw new ArgumentNullException("memoryCache"); if (cacheItemPolicy == null) throw new ArgumentNullException("cacheItemPolicy"); this.weakReference = new WeakReference(target); this.memoryCache = memoryCache; this.cacheItemPolicy = cacheItemPolicy; this.cacheKey = Guid.NewGuid().ToString(); this.memoryCache.Set(this.cacheKey, target, this.cacheItemPolicy); } /// <summary> /// 获取或设置当前软引用实例所引用的对象。 /// </summary> public T Target { get { T target = null; T targetFromCache = this.memoryCache.Get(this.cacheKey) as T; if (targetFromCache == null) //如果已从内存缓存移出 { T targetByReference = this.weakReference.Target as T; if (targetByReference != null) //但引用依然有效 { this.memoryCache.Set(this.cacheKey, targetByReference, this.cacheItemPolicy); //则继续受内存缓存管理 target = targetByReference; } } else { target = targetFromCache; } return target; } set { if (value == null) throw new ArgumentNullException("value"); this.weakReference.Target = value; this.memoryCache.Set(this.cacheKey, value, this.cacheItemPolicy); } } } }
上段代码看上去还行,不过接下来的这个就狗便了,不知有没好心人优化下,在
CachedWeakReferenceProperty
里:/// <summary> /// 获取或设置属性值,值的重建已线程安全。 /// </summary> public TProperty Value { get { TProperty value = null; if (this.isNull.HasValue && !this.isNull.Value && this.weakReference != null) value = this.weakReference.Target; if (this.owner.HasPersisted //只有已持久化才可能被重建 && (this.isNull == null || (this.isNull.HasValue && this.isNull.Value && value == null))) //从未重建 或 已被回收 则需重建 { lock (this) { if (this.isNull == null || (this.isNull.HasValue && this.isNull.Value && this.weakReference.Target == null)) { value = this.reconstructFunc(); this.isNull = value == null; this.weakReference = value == null ? null : new CachedWeakReference<TProperty>(value); } else //双检失败是因为锁前线程已重建 { if (this.weakReference != null) value = this.weakReference.Target; } } } return value; } set { this.isNull = value == null; this.weakReference = value == null ? null : new CachedWeakReference<TProperty>(value); } }
今天就分享到这吧,还有更多更让人开心的问题等着我们呢。