较为理想的延迟代理的编写方式
之前我谈到,在普通情况下我们可以很轻松地写出过一个代理类,用来处理延迟加载的情况。当时给出了一个很简单的做法,也就是指创建基类,覆盖它的一些属性实现,类似这种:
public class LazySomeClass : SomeClass
{
public override int SomeID
{
get
{
return this.LazySomeID.Value;
}
set
{
this.LazySomeID.Value = value;
}
}
public Lazy<int> LazySomeID { get; set; }
}
不过我当时也提到,这么做可能够用,但是也有一些缺点。例如,它破坏了SomeID属性中包含的业务逻辑。可能SomeID原本会包含一些验证逻辑,或和另外一个属性加以同步,或发起INotifyPropertyChanging/Changed中的事件。
因此我又想了想,理想中的延迟加载方式应该是什么样的呢?例如,同样是个SomeClass类,其中部分属性允许“设置”延迟加载:
public class SomeClass
{
public SomeClass() { }
public SomeClass(int i) { }
public virtual int LazyInt { get; set; }
public virtual bool LazyBoolean { get; set; }
public int EagerInt { get; set; }
public bool EagerBoolean { get; set; }
// some other members...
}
如果是一个较为合理的延迟代理类,我认为它的写法应该是这样的:
public class LazySomeClass : SomeClass
{
public override int LazyInt
{
get
{
if (!this.m_lazyIntLoaded)
{
if (this.m_lazyIntLoader != null)
{
base.LazyInt = this.m_lazyIntLoader();
this.m_lazyIntLoader = null;
}
this.m_lazyIntLoaded = true;
}
return base.LazyInt;
}
set
{
base.LazyInt = value;
this.m_lazyIntLoaded = true;
this.m_lazyIntLoader = null;
}
}
private bool m_lazyIntLoaded = false;
private Func<int> m_lazyIntLoader = null;
public Func<int> LazyIntLoader
{
get
{
return this.m_lazyIntLoader;
}
set
{
this.m_lazyIntLoader = value;
this.m_lazyIntLoaded = false;
}
}
}
如果我们需要为LazyInt属性设置延迟加载,那么可以设置LazyIntLoader属性,它是一个Func<int>委托对象。这种实现方式看上去复杂,不过它有一定的合理性:
- 每个Loader只执行一次,直到提供新的Loader。
- Loader执行后,会赋值给base.LazyInt,保持基类的业务逻辑。
- 从base.LazyInt读取,同样保持基类的业务逻辑。
- 如果不需要延迟加载,那么属性的行为保持不变。
其中第4点非常重要,这意味着这是一种可以“标准化”的延迟加载代理类的标准写法。我们可以在运行时使用Emit生成新的类型,继承目标类,为每个 virtual属性在子类中重写一份。由于在默认情况下属性的行为不会改变,因此这样的代理类不会有问题。甚至,“辅助类库”的接口我也想好了:
var builder = LazyFactory.Create(() => new SomeClass(10)
{
EagerInt = 10,
EagerBoolean = true
});
SomeClass value = builder
.Setup(c => c.LazyInt, () => GetLazyValue<int>())
.Setup(c => c.LazyBoolean, () => GetLazyValue<bool>())
.Create();
您有兴趣实现一下吗?
Ivony的精彩回复:
public class LazyLoader<T>
{
private Func<T> _loader;
private T _value;
private bool _loaded;
public LazyLoader(Func<T> loader)
{
_loader = loader;
}
public T GetValue()
{
if (!_loaded)
_value = _loader();
return _value;
}
public static implicit operator T(LazyLoader<T> loader)
{
if (loader == null)
return default(T);
return loader.GetValue();
}
public static implicit operator LazyLoader<T>(T value)
{
var loader = new LazyLoader<T>(null);
loader._value = value;
loader._loaded = true;
return loader;
}
public static implicit operator LazyLoader<T>(Func<T> loader)
{
return new LazyLoader<T>(loader);
}
}
{
private Func<T> _loader;
private T _value;
private bool _loaded;
public LazyLoader(Func<T> loader)
{
_loader = loader;
}
public T GetValue()
{
if (!_loaded)
_value = _loader();
return _value;
}
public static implicit operator T(LazyLoader<T> loader)
{
if (loader == null)
return default(T);
return loader.GetValue();
}
public static implicit operator LazyLoader<T>(T value)
{
var loader = new LazyLoader<T>(null);
loader._value = value;
loader._loaded = true;
return loader;
}
public static implicit operator LazyLoader<T>(Func<T> loader)
{
return new LazyLoader<T>(loader);
}
}
原文URL:http://www.cnblogs.com/JeffreyZhao/archive/2009/09/07/standard-lazy-proxy.html
浙公网安备 33010602011771号