一区二区久久-一区二区三区www-一区二区三区久久-一区二区三区久久精品-麻豆国产一区二区在线观看-麻豆国产视频

這下沒理由嫌Eval的性能差了吧?

好吧,你偏要說Eval性能差

  寫ASP.NET中使用Eval是再常見不過的手段了,好像任何一本ASP.NET書里都會(huì)描述如何把一個(gè)DataTable綁定到一個(gè)控件里去,并且通過Eval來取值的用法。不過在目前的DDD(Domain Driven Design)時(shí)代,我們操作的所操作的經(jīng)常是領(lǐng)域模型對(duì)象。我們可以把任何一個(gè)實(shí)現(xiàn)了IEnumerable的對(duì)象作為綁定控件的數(shù)據(jù)源,并且在綁定控件中通過Eval來獲取字段的值。如下:

protected void Page_Load(object sender, EventArgs e){    List<Comment> comments = GetComments();    this.rptComments.DataSource = comments;    this.rptComments.DataBind();}
<ASP:Repeater runat="server" ID="rptComments">    <ItemTemplate>        Title: <%# Eval("Title") %><br />        Conent: <%# Eval("Content") %>    ItemTemplate>    <SeparatorTemplate>        <hr />    SeparatorTemplate>ASP:Repeater>

  在這里,Eval對(duì)象就會(huì)通過反射來獲取Title和Content屬性的值。于是經(jīng)常就有人會(huì)見到說:“反射,性能多差啊,我可不用!”。在這里我還是對(duì)這種追求細(xì)枝末節(jié)性能的做法持保留態(tài)度。當(dāng)然,在上面的例子里我們的確可以換種寫法:

<ASP:Repeater runat="server" ID="rptComments">    <ItemTemplate>        Title: <%# (Container.DataItem as Comment).Title %><br />        Conent: <%# (Container.DataItem as Comment).Content %>    ItemTemplate>    <SeparatorTemplate>        <hr />    SeparatorTemplate>ASP:Repeater>

  我們通過Container.DataItem來獲取當(dāng)前遍歷過程中的數(shù)據(jù)對(duì)象,將其轉(zhuǎn)換成Comment之后讀取它的Title和Content屬性。雖然表達(dá)式有些長(zhǎng),但似乎也是個(gè)不錯(cuò)的解決方法。性能嘛……肯定是有所提高了。

  但是,在實(shí)際開發(fā)過程中,我們并不一定能夠如此輕松的將某個(gè)特定類型的數(shù)據(jù)作為數(shù)據(jù)源,往往需要組合兩種對(duì)象進(jìn)行聯(lián)合顯示。例如,我們?cè)陲@示評(píng)論列表時(shí)往往還會(huì)要顯示發(fā)表用戶的個(gè)人信息。由于C# 3.0中已經(jīng)支持了匿名對(duì)象,所以我們可以這樣做:

protected void Page_Load(object sender, EventArgs e){    List<Comment> comments = GetComments();    List<User> users = GetUsers();    this.rptComments.DataSource = from c in comments                                  from u in users                                  where c.UserID == u.UserID                                  order by c.CreateTime                                  select new                                  {                                      Title = c.Title,                                      Content = c.Content,                                      NickName = u.NickName                                  };    this.rptComments.DataBind();}

  我們通過LINQ級(jí)聯(lián)Comment和User數(shù)據(jù)集,可以輕松地構(gòu)造出構(gòu)造出作為數(shù)據(jù)源的匿名對(duì)象集合(有沒有看出LINQ的美妙?)。上面的匿名對(duì)象將包含Title,Content和NickName幾個(gè)公有屬性,因此在頁面中仍舊使用Eval來獲取數(shù)據(jù),不提。

  不過我?guī)缀蹩梢钥隙ǎ钟腥艘辛似饋恚?ldquo;LINQ沒有用!我們不用LINQ!Eval性能差!我們不用Eval!”。好吧,那么我免為其難地為他們用“最踏實(shí)”的技術(shù)重新實(shí)現(xiàn)一遍:

private Dictionary<int, User> m_users;protected User GetUser(int userId){    return this.m_users[userId];}protected void Page_Load(object sender, EventArgs e){    List<Comment> comments = GetComments();    List<User> users = GetUsers();    this.m_users = new Dictionary<int, User>();    foreach (User u in users)    {        this.m_users[u.UserID] = u;    }    this.rptComments.DataSource = comments;    this.rptComments.DataBind();}
<ASP:Repeater runat="server" ID="rptComments">    <ItemTemplate>        Title: <%# (Container.DataItem as Comment).Title %><br />        Conent: <%# (Container.DataItem as Comment).Content %><br />        NickName: <%# this.GetUser((Container.DataItem as Comment).UserID).NickName %>    ItemTemplate>    <SeparatorTemplate>        <hr />    SeparatorTemplate>ASP:Repeater>

  兄弟們自己做判斷吧。

嫌反射性能差?算有那么一點(diǎn)道理吧……

  反射速度慢?我同意它是相對(duì)慢一些。

  反射占CPU多?我同意他是相對(duì)多一點(diǎn)。

  所以Eval不該使用?我不同意——怎能把孩子和臟水一起倒了?我們把反射訪問屬性的性能問題解決不就行了嗎?

  性能差的原因在于Eval使用了反射,解決這類問題的傳統(tǒng)方法是使用Emit。但是.NET 3.5中現(xiàn)在已經(jīng)有了Lambda Expression,我們動(dòng)態(tài)構(gòu)造一個(gè)Lambda Expression之后可以通過它的Compile方法來獲得一個(gè)委托實(shí)例,至于Emit實(shí)現(xiàn)中的各種細(xì)節(jié)已經(jīng)由.NET框架實(shí)現(xiàn)了——這一切還真沒有太大難度了。

public class DynamicPropertyAccessor{    private Func<object, object> m_getter;    public DynamicPropertyAccessor(Type type, string propertyName)        : this(type.GetProperty(propertyName))    { }    public DynamicPropertyAccessor(PropertyInfo propertyInfo)    {        // target: (object)((({TargetType})instance).{Property})        // preparing parameter, object type        ParameterExpression instance = Expression.Parameter(            typeof(object), "instance");        // ({TargetType})instance        Expression instanceCast = Expression.Convert(            instance, propertyInfo.ReflectedType);        // (({TargetType})instance).{Property}        Expression propertyAccess = Expression.Property(            instanceCast, propertyInfo);        // (object)((({TargetType})instance).{Property})        UnaryExpression castPropertyValue = Expression.Convert(            propertyAccess, typeof(object));        // Lambda expression        Expression<Func<object, object>> lambda =             Expression.Lambda<Func<object, object>>(                castPropertyValue, instance);        this.m_getter = lambda.Compile();    }    public object GetValue(object o)    {        return this.m_getter(o);    }}

  在DynamicPropertyAccessor中,我們?yōu)橐粋€(gè)特定的屬性構(gòu)造一個(gè)形為o => object((Class)o).Property的Lambda表達(dá)式,它可以被Compile為一個(gè)Func

  這個(gè)方法是不是比較眼熟?沒錯(cuò),我在《方法的直接調(diào)用,反射調(diào)用與……Lambda表達(dá)式調(diào)用》一文中也使用了類似的做法。

測(cè)試一下性能?

  我們來比對(duì)一下屬性的直接獲取值,反射獲取值與……Lambda表達(dá)式獲取值三種方式之間的性能。

var t = new Temp { Value = null };PropertyInfo propertyInfo = t.GetType().GetProperty("Value");Stopwatch watch1 = new Stopwatch();watch1.Start();for (var i = 0; i < 1000000; i ++){    var value = propertyInfo.GetValue(t, null);}watch1.Stop();Console.WriteLine("Reflection: " + watch1.Elapsed);DynamicPropertyAccessor property = new DynamicPropertyAccessor(t.GetType(), "Value");Stopwatch watch2 = new Stopwatch();watch2.Start();for (var i = 0; i < 1000000; i++){    var value = property.GetValue(t);}watch2.Stop();Console.WriteLine("Lambda: " + watch2.Elapsed);Stopwatch watch3 = new Stopwatch();watch3.Start();for (var i = 0; i < 1000000; i++){    var value = t.Value;}watch3.Stop();Console.WriteLine("Direct: " + watch3.Elapsed);

  結(jié)果如下:

Reflection: 00:00:04.2695397Lambda: 00:00:00.0445277Direct: 00:00:00.0175414

  使用了DynamicPropertyAccessor之后,性能雖比直接調(diào)用略慢,也已經(jīng)有百倍的差距了。更值得一提的是,DynamicPropertyAccessor還支持對(duì)于匿名對(duì)象的屬性的取值。這意味著,我們的Eval方法完全可以依托在DynamicPropertyAccessor之上。

離快速Eval只有一步之遙了

  “一步之遙”?沒錯(cuò),那就是緩存。調(diào)用一個(gè)DynamicPropertyAccessor的GetValue方法很省時(shí),可是構(gòu)造一個(gè)DynamicPropertyAccessor對(duì)象卻非常耗時(shí)。因此我們需要對(duì)DynamicPropertyAccessor對(duì)象進(jìn)行緩存,如下:

public class DynamicPropertyAccessorCache{    private object m_mutex = new object();    private Dictionary<Type, Dictionary<string, DynamicPropertyAccessor>> m_cache =        new Dictionary<Type, Dictionary<string, DynamicPropertyAccessor>>();    public DynamicPropertyAccessor GetAccessor(Type type, string propertyName)    {        DynamicPropertyAccessor accessor;        Dictionary<string, DynamicPropertyAccessor> typeCache;        if (this.m_cache.TryGetValue(type, out typeCache))        {            if (typeCache.TryGetValue(propertyName, out accessor))            {                return accessor;            }        }        lock (m_mutex)        {            if (!this.m_cache.ContainsKey(type))            {                this.m_cache[type] = new Dictionary<string, DynamicPropertyAccessor>();            }            accessor = new DynamicPropertyAccessor(type, propertyName);            this.m_cache[type][propertyName] = accessor;            return accessor;        }    }}

  經(jīng)過測(cè)試之后發(fā)現(xiàn),由于每次都要從緩存中獲取DynamicPropertyAccessor對(duì)象,調(diào)用性能有所下降,但是依舊比反射調(diào)用要快幾十上百倍。

FastEval——還有人會(huì)拒絕嗎?

  FastEval方法,如果在之前的.NET版本中,我們可以將其定義在每個(gè)頁面的共同基類里。不過既然我們?cè)谟?NET 3.5,我們可以使用Extension Method這種沒有任何侵入的方式來實(shí)現(xiàn):

public static class FastEvalExtensions{    private static DynamicPropertyAccessorCache s_cache =         new DynamicPropertyAccessorCache();    public static object FastEval(this Control control, object o, string propertyName)    {        return s_cache.GetAccessor(o.GetType(), propertyName).GetValue(o);    }    public static object FastEval(this TemplateControl control, string propertyName)    {        return control.FastEval(control.Page.GetDataItem(), propertyName);    }}

  我們?cè)贑ontrol上的擴(kuò)展,確保了每個(gè)頁面中都可以直接通過一個(gè)對(duì)象和屬性名獲取一個(gè)值。而在TemplateControl上的擴(kuò)展,則使得各類可以綁定控件或頁面(Page,MasterPage,UserControl)都可以直接通過屬性名來獲取當(dāng)前正在綁定的那個(gè)數(shù)據(jù)對(duì)象里的屬性值。

  現(xiàn)在,您還有什么理由拒絕FastEval?

其他

  其實(shí)我們整篇文章都小看了Eval方法的作用。Eval方法的字符串參數(shù)名為“expression”,也就是表達(dá)式。事實(shí)上我們甚至可以使用“.”來分割字符串以獲取一個(gè)對(duì)象深層次的屬性,例如<%# Eval("Content.Length") %>。那么我們的FastEval可以做到這一點(diǎn)嗎?當(dāng)然可以——只不過這需要您自己來實(shí)現(xiàn)了。:)

  最后再留一個(gè)問題供大家思考:現(xiàn)在DynamicPropertyAccessor只提供一個(gè)GetValue方法,那么您能否為其添加一個(gè)SetValue方法來設(shè)置這個(gè)屬性呢?希望大家踴躍回復(fù),稍后我將提供我的做法。

思考題解答

  有一點(diǎn)大家應(yīng)該知道,一個(gè)屬性其實(shí)是由一對(duì)get/set方法組成(當(dāng)然可能缺少其中一個(gè))。而獲取了一個(gè)屬性的PropertyInfo對(duì)象之后,可以通過它的GetSetMethod方法來獲取它的設(shè)置方法。接下來的工作,不就可以完全交給《方法的直接調(diào)用,反射調(diào)用與……Lambda表達(dá)式調(diào)用》一文里的DynamicMethodExecutor了嗎?因此為DynamicPropertyAccessor添加一個(gè)SetValue方法也很簡(jiǎn)單:

public class DynamicPropertyAccessor{    ...    private DynamicMethodExecutor m_dynamicSetter;    ...    public DynamicPropertyAccessor(PropertyInfo propertyInfo)    {        ...        MethodInfo setMethod = propertyInfo.GetSetMethod();        if (setMethod != null)        {            this.m_dynamicSetter = new DynamicMethodExecutor(setMethod);        }    }    ...    public void SetValue(object o, object value)    {        if (this.m_dynamicSetter == null)        {            throw new NotSupportedException("Cannot set the property.");        }        this.m_dynamicSetter.Execute(o, new object[] { value });    }}

  在下面的評(píng)論中,Such Cloud已經(jīng)想到了類似的做法,值得鼓勵(lì),同時(shí)多謝支持。

NET技術(shù)這下沒理由嫌Eval的性能差了吧?,轉(zhuǎn)載需保留來源!

鄭重聲明:本文版權(quán)歸原作者所有,轉(zhuǎn)載文章僅為傳播更多信息之目的,如作者信息標(biāo)記有誤,請(qǐng)第一時(shí)間聯(lián)系我們修改或刪除,多謝。

主站蜘蛛池模板: 性欧美巨大 | 国产高级黄区18勿进一区二区 | 欧美成人二区 | 中文字幕成人网 | 福利在线观看 | 成人国产第一区在线观看 | 亚洲天堂图片 | 五月天六月婷婷开心激情 | 日韩a无吗一区二区三区 | 色哟哟在线视频 | 日本二三区 | 国产亚洲精品福利 | 夜色福利院在线看青草一 | 久久性| 99在线观看精品视频 | 加勒比一区| 亚洲成a人片77777老司机 | 狠狠一区 | caoporn国产精品免费视频 | 国产麻豆精品视频 | 黄 色 免 费 网站在线观看 | 中国一级特黄 | 国产精欧美一区二区三区 | 日韩午夜激情视频 | 中文字幕一区二区三区四区 | 国产好吊妞视频在线观看 | 久久婷婷午色综合夜啪 | 久久综合香蕉久久久久久久 | 亚洲第一免费视频 | 国产在线一区二区视频 | 国产精品久久自在自2021 | 亚洲香蕉伊在人在线观看9 亚洲香蕉伊综合在人在线 亚洲香蕉影院 | 国产在线视频二区 | 精品99视频| 福利一区二区在线观看 | 亚洲一区2区三区4区5区 | 久久精品99视频 | 国语精品视频在线观看不卡 | 国产视频一区二区三区四区 | 久久久亚洲天堂 | 亚洲一区二区免费看 |