现在的位置: 首页 > 综合 > 正文

来一点反射,再来一点Emit —— 极度简化Entity!

2013年01月16日 ⁄ 综合 ⁄ 共 5280字 ⁄ 字号 评论关闭
文章目录

在前一篇文章《没有ORM或代码生成数据就不能持久化了? - 用范型技术代替代码生成!》中,Teddy尝试运用泛型极大简化了一个轻量级持久化框架对代码生成的依赖,并且为了保证性能,整个持久化组件没有使用反射。在本文中,Teddy将在保证性能的基础上,加一点反射和加一点Emit,从而进一步简化Entity的定义和使用,当然也就进一步降低了组件对传统代码生成的依赖。读者可以对比前文阅读本文,看看改进的效果。 内容绝对精彩,不容错过哟!

来一点反射

在之前方案的Entity定义中,每个具体的Entity类必须从基类Entity<EntityType>继承,但是Entity<EntityType>只是提供了一组公共方法,并不涉及Entity的构造过程。我们还是把之前的Entity示例代码再列一下:

using System;

namespace Ilungasoft.Helper.TestApp.DomainObject
{
    
public class About : Ilungasoft.Helper.Data.Entity<About>
    
{
        
public About()
        
{
            keyValues.Add(
"ID", DefaultValue<int>());
            keyValues.Add(
"Title", DefaultValue<string>());
            keyValues.Add(
"Content", DefaultValue<string>());
            keyValues.Add(
"Deletable", DefaultValue<bool>());
            keyValues.Add(
"Order", DefaultValue<int>());
        }


        
public int ID
        
{
            
get return (int)keyValues["ID"]; }
            
set { keyValues["ID"= value; }
        }


        
public string Title
        
{
            
get return (string)keyValues["Title"]; }
            
set { keyValues["Title"= value; }
        }


        
public string Content
        
{
            
get return (string)keyValues["Content"]; }
            
set { keyValues["Content"= value; }
        }


        
public bool Deletable
        
{
            
get return (bool)keyValues["Deletable"]; }
            
set { keyValues["Deletable"= value; }
        }


        
public int Order
        
{
            
get return (int)keyValues["Order"]; }
            
set { keyValues["Order"= value; }
        }

    }

}

以上代码有什么不爽的地方呢?首先就是About()这个构造函数。为了避免反射,所以通过在构造函数中显式指定属性名称和类型。但是,很显然,在下面的属性定义中,已经指定过属性及其类型了。指定了两遍,说明存在冗余,也就有必要改进。那就让我们在基类Entity<EntityType>中通过反射来获取属性信息好了。这样一来,About这个类可以被简化为:

using System;

namespace Ilungasoft.Helper.TestApp.DomainObject
{
    
public class About : Ilungasoft.Helper.Data.Entity<About>
    
{
        
public About() : base()
        
{
        }


        
public int ID
        
{
            
get return (int)keyValues["ID"]; }
            
set { keyValues["ID"= value; }
        }


        
public string Title
        
{
            
get return (string)keyValues["Title"]; }
            
set { keyValues["Title"= value; }
        }


        
public string Content
        
{
            
get return (string)keyValues["Content"]; }
            
set { keyValues["Content"= value; }
        }


        
public bool Deletable
        
{
            
get return (bool)keyValues["Deletable"]; }
            
set { keyValues["Deletable"= value; }
        }


        
public int Order
        
{
            
get return (int)keyValues["Order"]; }
            
set { keyValues["Order"= value; }
        }

    }

}

注意“: base()”,构造函数调用基类构造函数来构造。那么,基类Entity<EntityType>会有怎样的改动呢?

    public class Entity<EntityType> : IEntity
        where EntityType : Entity
<EntityType>new()
    
{
        
protected static Dictionary<stringobject> keyValuesTemplate = null;

        
/// <summary>
        
/// Initializes a new instance of the <see cref="T:Entity&lt;EntityType&gt;"/> class.
        
/// </summary>

        public Entity()
        
{
            
if (keyValuesTemplate == null)
            
{
                keyValuesTemplate 
= new Dictionary<stringobject>();
                PropertyInfo[] pis 
= typeof(EntityType).GetProperties();
                
foreach (PropertyInfo pi in pis)
                
{
                    keyValuesTemplate.Add(pi.Name, 
this.GetType().GetMethod("DefaultValue", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(pi.PropertyType).Invoke(thisnull));
                }

            }


            keyValues 
= new Dictionary<stringobject>(keyValuesTemplate);
        }


        
protected Dictionary<stringobject> keyValues;

        
/// <summary>
        
/// Defaults the value.
        
/// </summary>
        
/// <returns></returns>

        protected object DefaultValue<MemberType>()
        
{
            
if (typeof(MemberType) == typeof(string))
            
{
                
return string.Empty;
            }

            
return default(MemberType);
        }


        
//省略其他代码。。。
    }

上面的代码还是挺有意思的,让我来给您解释一二。

首先,基类构造函数只在第一次构造About时通过反射构造keyValues对象的模版,之后的实例化只是简单的Copy改模版从而将反射对性能的影响降至最低。

接着,注意DefaultValue<MemberType>()这个函数,该函数用于指定属性的缺省值,是利用了C#2.0新关键字default来实现的。default关键字真的极大方便了我们设置缺省值,原来一般只能自己写NullObject,现在只需default(Type)就行。只不过,default关键字是通过一定的泛型机制实现的,也就是说它的参数Type必须是一个类型声明符,而不是一个Type类型的实例。这会有什么问题呢?原来在具体的Entity类的构造函数中显式指定的话没问题,因为可以直接指定类型声明符,如:“DefaultValue<int>()”,但是,如果改成通过利用反射来实现该怎么办呢?注意下面这条语句:

this.GetType().GetMethod("DefaultValue", BindingFlags.Instance | BindingFlags.NonPublic).MakeGenericMethod(pi.PropertyType).Invoke(thisnull)

这条语句实际上就相当于“DefaultValue<int>()”调用,只是,“DefaultValue<int>()”中类型int必须显式指定,必须传类型声明符,而反射类型PropertyInfo.PropertyType却只是一个Type实例对象。所以,此时,就必须通过反射调用DefaultValue<MemberType>()方法了。(这可不可以作为一个在反射中调用default关键字的典型范例呢?^-^)。这里指定参数“BindingFlags.Instance | BindingFlags.NonPublic”是因为DefaultValue方法是protected的,必须指定该参数才能通过GetMethod()获得引用。如果该方法定义成public的,则不需指定该参数。

再来一点Emit

上面,我们通过很少的反射,把构造函数中的代码冗余给去除了。那么,让我们继续看看去除构造函数中的代码冗余后的Entity类的定义还有什么不爽的地方呢?

不爽的地方还不少:首先是keyValues这个类暴露了太多基类的实现细节,一个突兀的keyValues,让Entity类乍一眼看去不太容易让人明白;其次,每个属性都要用这种枯燥、类似的方式定义get,set,并从keyValues中读写属性值,怎么看怎么重复,怎么看怎么来的写这样类似的代码,是可以用工具来生成这个实体类,但是会让我更不爽,只生成这点代码,却要我承受代码生成带来的维护成本,实在是不值,太不值了。让我们再瞟一眼本段的小标题“再来一点Emit”,没错,我要让Emit出马了。先来点美丽的远景好了,我希望利用Emit改造过后,我可以这样来定义一个Entity类:

using System;

抱歉!评论已关闭.