代码改变世界

如果是能简单解决的问题,就不用想得太复杂了

2009-09-05 17:41  Jeffrey Zhao  阅读(7556)  评论(34编辑  收藏  举报

有个朋友在MSN问我说,有没有关于Emit的资料,它想生成一个类的动态代理。他抱怨Emit还是很麻烦,不过交谈过后知道他是想要做什么。他希望为一个对象的某个属性作延迟加载,这样可以避免一些无谓的消耗。例如:

public class SomeClass
{
    public int SomeID { get; set; }

    // some other members...
}

原本构造一个SomeClass时可以这样:

var someClass = new SomeClass();
someClass.SomeID = GetSomeID();
Process(someClass);

但是由于Process方法中可能不需要用到SomeID属性,于是在外部调用的GetSomeID方法可能就形成了无谓的性能损耗。一个常见的做法方式可能就是进行延迟加载了。那位朋友的意思是先把SomeID标为virtual:

public class SomeClass
{
    public virtual int SomeID { get; set; }

    // some other members...
}

然后使用Emit来生成一个动态类型,继承SomeClass,override掉SomeID属性,形成延迟加载。不过我提出,这个方法是不是太重了,因为动态代理不是那么孤立存在的,它往往需要考虑很多其他东西。例如缓存动态类型,例如,对于相同类型一个成员或多个成员的延迟加载,使生成一个通用的动态类型,还是多个动态类型。例如……怎么样的API是最合适的?

所以,如果只是简单的情况下,不如直接手动来实现这样的延迟效果:

public class LazySomeClass : SomeClass
{
    public override int SomeID
    {
        get
        {
            return this.LazySomeID.Value;
        }
        set
        {
            this.LazySomeID.Value = value;
        }
    }

    public Lazy<int> LazySomeID { get; set; }
}

于是在使用的时候就可以:

var someClass = new LazySomeClass();
someClass.LazySomeID = new Lazy<int>(() => GetSomeID());
Process(someClass);

这样其实就可以在一定程度上达到目的了。Lazy类的原理在之前也有过提及(这里需要些修改),这是一种简单但有用的类型。其实在项目的许多情况下,我们这么做也足够了。不需要复杂的方法,不需要复杂的Emit。不过如果您是为了锻炼能力,或者由于项目中此类需求特别多,想设计一个通用的的类库,这也不错。

当然,上面的实现也有缺陷,因为它不是最理想、最完整、最通用的延迟加载代理类(为什么?)。如果您感兴趣,也可以想象一个完美的代理类应该是什么样子的,甚至给出一个通用的辅助类库。

哦,对了,NHibernate的做法其实也不完美,有机会我会分析一下,并阐述我的看法的。