Spiga

这下没理由嫌Eval的性能差了吧?

2009-01-09 02:32 by Jeffrey Zhao, 14617 visits, 收藏, 编辑

Updated:提供思考题解答

好吧,你偏要说Eval性能差

写ASP.NET中使用Eval是再常见不过的手段了,好像任何一本ASP.NET书里都会描述如何把一个DataTable绑定到一个控件里去,并且通过Eval来取值的用法。不过在目前的DDD(Domain Driven Design)时代,我们操作的所操作的经常是领域模型对象。我们可以把任何一个实现了IEnumerable的对象作为绑定控件的数据源,并且在绑定控件中通过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对象就会通过反射来获取Title和Content属性的值。于是经常就有人会见到说:“反射,性能多差啊,我可不用!”。在这里我还是对这种追求细枝末节性能的做法持保留态度。当然,在上面的例子里我们的确可以换种写法:

<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来获取当前遍历过程中的数据对象,将其转换成Comment之后读取它的Title和Content属性。虽然表达式有些长,但似乎也是个不错的解决方法。性能嘛……肯定是有所提高了。

但是,在实际开发过程中,我们并不一定能够如此轻松的将某个特定类型的数据作为数据源,往往需要组合两种对象进行联合显示。例如,我们在显示评论列表时往往还会要显示发表用户的个人信息。由于C# 3.0中已经支持了匿名对象,所以我们可以这样做:

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级联Comment和User数据集,可以轻松地构造出构造出作为数据源的匿名对象集合(有没有看出LINQ的美妙?)。上面的匿名对象将包含Title,Content和NickName几个公有属性,因此在页面中仍旧使用Eval来获取数据,不提。

不过我几乎可以肯定,又有人要叫了起来:“LINQ没有用!我们不用LINQ!Eval性能差!我们不用Eval!”。好吧,那么我免为其难地为他们用“最踏实”的技术重新实现一遍:

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>

兄弟们自己做判断吧。

嫌反射性能差?算有那么一点道理吧……

反射速度慢?我同意它是相对慢一些。

反射占CPU多?我同意他是相对多一点。

所以Eval不该使用?我不同意——怎能把孩子和脏水一起倒了?我们把反射访问属性的性能问题解决不就行了吗?

性能差的原因在于Eval使用了反射,解决这类问题的传统方法是使用Emit。但是.NET 3.5中现在已经有了Lambda Expression,我们动态构造一个Lambda Expression之后可以通过它的Compile方法来获得一个委托实例,至于Emit实现中的各种细节已经由.NET框架实现了——这一切还真没有太大难度了。

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中,我们为一个特定的属性构造一个形为o => object((Class)o).Property的Lambda表达式,它可以被Compile为一个Func<object, object>委托。最终我们可以通过为GetValue方法传入一个Class类型的对象来获取那个指定属性的值。

这个方法是不是比较眼熟?没错,我在《方法的直接调用,反射调用与……Lambda表达式调用》一文中也使用了类似的做法。

测试一下性能?

我们来比对一下属性的直接获取值,反射获取值与……Lambda表达式获取值三种方式之间的性能。

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);

结果如下:

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

使用了DynamicPropertyAccessor之后,性能虽比直接调用略慢,也已经有百倍的差距了。更值得一提的是,DynamicPropertyAccessor还支持对于匿名对象的属性的取值。这意味着,我们的Eval方法完全可以依托在DynamicPropertyAccessor之上。

离快速Eval只有一步之遥了

“一步之遥”?没错,那就是缓存。调用一个DynamicPropertyAccessor的GetValue方法很省时,可是构造一个DynamicPropertyAccessor对象却非常耗时。因此我们需要对DynamicPropertyAccessor对象进行缓存,如下:

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;
        }
    }
}

经过测试之后发现,由于每次都要从缓存中获取DynamicPropertyAccessor对象,调用性能有所下降,但是依旧比反射调用要快几十上百倍。

FastEval——还有人会拒绝吗?

FastEval方法,如果在之前的.NET版本中,我们可以将其定义在每个页面的共同基类里。不过既然我们在用.NET 3.5,我们可以使用Extension Method这种没有任何侵入的方式来实现:

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);
    }
}

我们在Control上的扩展,确保了每个页面中都可以直接通过一个对象和属性名获取一个值。而在TemplateControl上的扩展,则使得各类可以绑定控件或页面(Page,MasterPage,UserControl)都可以直接通过属性名来获取当前正在绑定的那个数据对象里的属性值。

现在,您还有什么理由拒绝FastEval?

其他

其实我们整篇文章都小看了Eval方法的作用。Eval方法的字符串参数名为“expression”,也就是表达式。事实上我们甚至可以使用“.”来分割字符串以获取一个对象深层次的属性,例如<%# Eval("Content.Length") %>。那么我们的FastEval可以做到这一点吗?当然可以——只不过这需要您自己来实现了。:)

最后再留一个问题供大家思考:现在DynamicPropertyAccessor只提供一个GetValue方法,那么您能否为其添加一个SetValue方法来设置这个属性呢?希望大家踊跃回复,稍后我将提供我的做法。

思考题解答

有一点大家应该知道,一个属性其实是由一对get/set方法组成(当然可能缺少其中一个)。而获取了一个属性的PropertyInfo对象之后,可以通过它的GetSetMethod方法来获取它的设置方法。接下来的工作,不就可以完全交给《方法的直接调用,反射调用与……Lambda表达式调用》一文里的DynamicMethodExecutor了吗?因此为DynamicPropertyAccessor添加一个SetValue方法也很简单:

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 });
    }
}

在下面的评论中,Such Cloud已经想到了类似的做法,值得鼓励,同时多谢支持。

标签: LINQ, 性能, Eval
Add your comment

109 条回复

    评论共2页: 上一页 1 2 
  1. #10楼 Astar      2009-01-08 16:46
    学习
     回复 引用 查看   
  2. #11楼 ThinkQ[未注册用户]2009-01-08 16:49
    看来还有许多.net知识需要学习
     回复 引用   
  3. #12楼 凌军      2009-01-08 17:04
    根本没注意到Eval里面还有这么多学问...惭愧...
     回复 引用 查看   
  4. #13楼 Clingingboy      2009-01-08 17:15
    要是没有反射,.net功能就大大缩水了.
    asp.net Mvc框架不也到处的反射.当然也要看应用的.
     回复 引用 查看   
  5. #14楼[楼主] Jeffrey Zhao      2009-01-08 17:17
    @Clingingboy
    这话我同意
     回复 引用 查看   
  6. #15楼 andy.wu      2009-01-08 17:29
    强烈支持反射。

    为了FastEval,还得多耗我的精力。大多数情况下不值啊。

    但楼主的方法不错,值得学习,赞一个。
     回复 引用 查看   
  7. #16楼[楼主] Jeffrey Zhao      2009-01-08 17:31
    @andy.wu
    哪里多耗你精力了?一个扩展,一劳永逸。平均到每次调用可以忽略不计。
     回复 引用 查看   
  8. #17楼 GUO Xingwang      2009-01-08 17:43
    老赵擅长从小的地方发现问题并能找到解决方案,非常值得学习。
    现在看你的文章感觉很累,但是我知道必须要看,因为里面有很多好东西!
    下班以后好好品一下。
     回复 引用 查看   
  9. #18楼 joylee      2009-01-08 17:57
    我一般都是用Eval绑定的,可好多人都说使用服务器控件速度慢,
    1.真的可以整站不使用服务器控件吗?至少<form>要runat=server吧
    2.如果不用EVAL绑定数据那不就只有用string 拼接HTML了,还有其他方法吗??

    望老赵或其他高手赐教,不甚感激!!!!
     回复 引用 查看   
  10. #19楼 朝晖的.net      2009-01-08 17:57
    这篇挺不错的,受益匪浅。

    看着这个文章感觉偶要学习的东西还有好多啊。

    感觉老赵进入2009后迅速活跃起来了。
     回复 引用 查看   
  11. #20楼 阿不      2009-01-08 17:59
    老赵的表达式功力很深厚啊
     回复 引用 查看   
  12. #21楼[楼主] Jeffrey Zhao      2009-01-08 18:04
    --引用--------------------------------------------------
    joylee: 我一般都是用Eval绑定的,可好多人都说使用服务器控件速度慢,
    1.真的可以整站不使用服务器控件吗?至少&lt;form&gt;要runat=server吧
    2.如果不用EVAL绑定数据那不就只有用string 拼接HTML了,还有其他方法吗??
    --------------------------------------------------------
    说慢的人,基本上是扯淡的。真正知道怎么开发高性能web应用程序的人不会在这种无所谓的小地方纠缠。
     回复 引用 查看   
  13. #22楼 Bēniaǒ      2009-01-08 18:06
    很好很强大.

    老赵你这方法确实不错,以前用Eval通过反射整,后来在你那篇<<使用UserControl生成html>>文章写出来看过后,就是使用的后面那种绑定数据了.
     回复 引用 查看   
  14. #23楼[楼主] Jeffrey Zhao      2009-01-08 18:06
    @要有好的心情
    都是内部类啊,不过我会比较一下看看的,呵呵。
     回复 引用 查看   
  15. #24楼[楼主] Jeffrey Zhao      2009-01-08 18:07
    @Bēniaǒ
    类型转化+属性访问的也有好处,就是可以通过预编译aspx检查错误。
     回复 引用 查看   
  16. #25楼[楼主] Jeffrey Zhao      2009-01-08 18:08
    --引用--------------------------------------------------
    朝晖的.net: 这篇挺不错的,受益匪浅。
    看着这个文章感觉偶要学习的东西还有好多啊。
    感觉老赵进入2009后迅速活跃起来了。
    --------------------------------------------------------
    老赵离江郎才尽永远只有3篇文章。
     回复 引用 查看   
  17. #26楼 玉开      2009-01-08 18:13
    不错呀。
     回复 引用 查看   
  18. #27楼 Jerry Qian      2009-01-08 19:58
    老赵你这是level 几百的.你的文章我看得都挺有意思.可是一直是似懂非懂的.受措了.
     回复 引用 查看   
  19. #28楼 Such Cloud      2009-01-08 20:10
    终于等到这篇文章了
     回复 引用 查看   
  20. #29楼 kkun      2009-01-08 20:23
    赵老师您好!
    又来问跑题问题了,文章刚看完,其中一段代码这样写到
    public DynamicPropertyAccessor(Type type, string propertyName)
    : this(type.GetProperty(propertyName))
    不太理解的说,在哪里有介绍这种写法的资料呀,
     回复 引用 查看   
  21. #30楼 怪怪      2009-01-08 20:25
    缓存的问题,可以用脑袋说的静态类型字典(借助泛型)解决。

    另外我不同意你说的细枝末节的性能。如Knuth所说,程序是编写一遍,运行很多次,哪怕一个时钟周期也是值得的。当然我并不贯彻这个做法,那是因为确实是能力有限,顾不了那么多了....
     回复 引用 查看   
  22. #31楼 kkun      2009-01-08 20:38
    现在超佩服赵老师的功力!~努力学习ing!
     回复 引用 查看   
  23. #32楼 bbb[未注册用户]2009-01-08 21:25
    怎能把孩子和脏水一起倒了?
     回复 引用   
  24. #33楼 zbmwt[未注册用户]2009-01-08 22:39
    很有参考价值,不过有个问题。var t = new Temp { Value = null };
    这个测试的数据似乎太简单了,是否可以搞一个更复杂的进行测试?实际项目中,不可能只用这么简单的变量的。
     回复 引用   
  25. #34楼 天生俪姿      2009-01-08 22:40
    虽说我也是没有太看完文章,不过很多的东西真的是还看不懂呵呵。挺不错的。学习中。。。
    继续支持老赵。

    老赵离江郎才尽永远只有3篇文章。
    这句当什么讲呢?老赵。
     回复 引用 查看   
  26. #35楼 Apple Yang      2009-01-08 22:43
    好一本武林秘籍。
     回复 引用 查看   
  27. #36楼[楼主] Jeffrey Zhao      2009-01-08 23:15
    --引用--------------------------------------------------
    怪怪: 缓存的问题,可以用脑袋说的静态类型字典(借助泛型)解决。

    另外我不同意你说的细枝末节的性能。如Knuth所说,程序是编写一遍,运行很多次,哪怕一个时钟周期也是值得的。当然我并不贯彻这个做法,那是因为确实是能力有限,顾不了那么多了....
    --------------------------------------------------------
    关于缓存使用静态字典,莫非就是我现在用的做法?我一直用这种方法的,好像也是最常用的做法。
    追求细致末节性能当然是可以的,但是不能为此抛弃其他的,比如优雅,易于编写等等。如果追求性能应该去优化瓶颈,当然如果属于不会影响编码,一次编写多次运行也是好的——就像我现在这样,呵呵。
     回复 引用 查看   
  28. #37楼[楼主] Jeffrey Zhao      2009-01-08 23:16
    --引用--------------------------------------------------
    天生俪姿:
    老赵离江郎才尽永远只有3篇文章。
    这句当什么讲呢?老赵。
    --------------------------------------------------------
    如果停止思考或失去灵感,就只能写3篇文章。
     回复 引用 查看   
  29. #38楼[楼主] Jeffrey Zhao      2009-01-08 23:19
    --引用--------------------------------------------------
    zbmwt: 很有参考价值,不过有个问题。var t = new Temp { Value = null };
    这个测试的数据似乎太简单了,是否可以搞一个更复杂的进行测试?实际项目中,不可能只用这么简单的变量的。
    --------------------------------------------------------
    再复杂不也是在访问属性吗?这种简单示例确保性能比较时完全是由访问属性的方式决定的,如果一个属性访问一次本身就要很长时间,那么测试时反而无法关注主要矛盾了。
     回复 引用 查看   
  30. #39楼[楼主] Jeffrey Zhao      2009-01-08 23:19
    --引用--------------------------------------------------
    kkun: 赵老师您好!
    又来问跑题问题了,文章刚看完,其中一段代码这样写到
    public DynamicPropertyAccessor(Type type, string propertyName)
    : this(type.GetProperty(propertyName))
    不太理解的说,在哪里有介绍这种写法的资料呀,
    --------------------------------------------------------
    你不了解C#语法啊……自己看文档吧。
     回复 引用 查看   
  31. #40楼 ITAres      2009-01-08 23:20
    有个问题想问老赵,希望老赵可以回答我:
    MethodInfo methodInfo = typeof(Program).GetMethod("Call");
    -----------我一直以为这一步的时候就进行了反射操作,看你的文章应该是没有的....

    methodInfo.Invoke(program, parameters);
    ------------你的意思是这一步的时候才进行了反射,是吗?
     回复 引用 查看   
  32. #41楼[楼主] Jeffrey Zhao      2009-01-08 23:20
    --引用--------------------------------------------------
    bbb: 怎能把孩子和脏水一起倒了?
    --------------------------------------------------------
    因为Eval这个性能问题但是把优雅简单的编码方式也放弃了。
     回复 引用 查看   
  33. #42楼[楼主] Jeffrey Zhao      2009-01-08 23:22
    --引用--------------------------------------------------
    ITAres: 有个问题想问老赵,希望老赵可以回答我:
    MethodInfo methodInfo = typeof(Program).GetMethod("Call");
    -----------我一直以为这一步的时候就进行了反射操作,看你的文章应该是没有的....

    methodInfo.Invoke(program, parameters);
    ------------你的意思是这一步的时候才进行了反射,是吗?
    --------------------------------------------------------
    GetMethod是反射,不过这种方法其实很省时,而且完全可以获取一次后反复调用无数次,就不考虑进去了。真正耗时的是Invoke操作。
     回复 引用 查看   
  34. #43楼 ITAres      2009-01-08 23:25
    @Jeffrey Zhao
    老赵天才啊
     回复 引用 查看   
  35. #44楼 Such Cloud      2009-01-09 00:24
    终于脑子转过弯了 把老赵的作业做出来 贴出来 大家看看有没有更好的方法
    public class DynamicPropertyAccessor
    {
    private Func<object, object> m_getter;
    private Action<Temp,object> m_setter;
    private Temp entity;

    public DynamicPropertyAccessor(Temp t, string propertyName)
    {
    this.entity = t;
    Type type = t.GetType();

    PropertyInfo propertyInfo = type.GetProperty(propertyName);

    // 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();

    ParameterExpression paramEntityObj = Expression.Parameter(type, "t");
    ParameterExpression paramProVal = Expression.Parameter(typeof(object), "propertyVal");
    UnaryExpression paramProVal2 = Expression.Convert(paramProVal, typeof(object));
    MemberExpression me = Expression.Property(paramEntityObj, "Value");

    MethodCallExpression body = Expression.Call(paramEntityObj, type.GetMethod("set_" + propertyInfo.Name), paramProVal2);
    Expression<Action<Temp, object>> setPropertyExpression = Expression.Lambda<Action<Temp, object>>(body, paramEntityObj, paramProVal);

    Action<Temp, object> setPropertyAction = setPropertyExpression.Compile();

    this.m_setter = setPropertyExpression.Compile();

    }

    public object GetValue(object o)
    {
    return this.m_getter(o);
    }

    public void SetValue(object o)
    {
    this.m_setter(entity,o);
    }
    }

    调用
    DynamicPropertyAccessor property = new DynamicPropertyAccessor(t, "Value");
    property.SetValue("sddd");
    Console.WriteLine(t.Value.ToString());
     回复 引用 查看   
  36. #45楼 davin      2009-01-09 00:27
    又见赵老师佳作了!看来读1,2遍是不能完全明白的,呵呵.在大约2个小时之前看了一篇老外Terry Aney写的 LINQ to SQLBatch Updates and Deletes 的文章,细看下去才知道terry 写的这篇文章(做了补充和改进)最初源自你的英文blog(很久没更新了哈).不由得激动了一下,可是第一次(可能我的阅读量太少)看到老外们从我们的blog上获取资源了.愿老赵再接再厉,我希望能能看到更多类似于 In Jeffrey's article ...。
     回复 引用 查看   
  37. #46楼 davin      2009-01-09 00:45
    临睡前想到一个问题,如你文中所指"可以获取对象生层次的属性<%# Eval("Content.Length") %>"这个属性包括也包括entity.refEntity.property(对象的关联对象的属性),当然这里只考虑实体对象的1:1或n:1的关系而?如果包括,那我该如何入手,望指点一二!
     回复 引用 查看   
  38. #47楼 怪怪      2009-01-09 02:00
    @Jeffrey Zhao
    呃,你是指你文章中的吗? 那我指的不是这种,而是“把泛型当作类型字典用”的方法,不过后面这种方法就要求调用时必须明确指明类型了。这也是脑袋好几年前泛型刚出没多久时候弄的了,也不太新鲜了。

    关于性能的话题,大多数时候性能的问题和任何其它问题一样,都是因为代码对事物的描述有偏差,所以才产生,哪怕是细枝末节的。只是不严重的时候我们就不去管它了。

    包括这个Eval和其它一些各种各样的损失(不仅仅是性能上的),实质上是因为ASP.NET中的一些模型离真正的贴切描述相差一步,其实现又离设计者的构思相差一步,才造成的。

    不煞风景了,总而言之,你这个实现确实要比玩IL舒服也安全,顶你一下呵呵。
     回复 引用 查看   
  39. #48楼[楼主] Jeffrey Zhao      2009-01-09 02:12
    @davin
    没有听懂你的问题,再说一下?
     回复 引用 查看   
  40. #49楼[楼主] Jeffrey Zhao      2009-01-09 02:13
    @怪怪
    哎,我还是没有跟上你的高度,呵呵……
     回复 引用 查看   
  41. #50楼 oec2003      2009-01-09 08:34
    马上上班了 ,回家再看
     回复 引用 查看   
  42. #51楼 啥事[未注册用户]2009-01-09 09:03
    老赵好像又胖了
     回复 引用   
  43. #52楼 菌哥      2009-01-09 09:05
    先支持一下,再来慢慢品尝佳作!
     回复 引用 查看   
  44. #53楼[楼主] Jeffrey Zhao      2009-01-09 09:06
    --引用--------------------------------------------------
    啥事: 老赵好像又胖了
    --------------------------------------------------------
    搞笑了……
     回复 引用 查看   
  45. #54楼 BZZ      2009-01-09 09:08
    老赵有点像SKY
     回复 引用 查看   
  46. #55楼 不要怪我太坦白[未注册用户]2009-01-09 09:14
    老赵,这篇文章你是昨天写的,
    今天又修改了一下,难道就是为了上“今日头条”?
     回复 引用   
  47. #56楼 自强不息      2009-01-09 09:14
    不用Eval,还有别的方法绑定吗?还请各位明示
     回复 引用 查看   
  48. #57楼[楼主] Jeffrey Zhao      2009-01-09 09:18
    --引用--------------------------------------------------
    不要怪我太坦白: 老赵,这篇文章你是昨天写的,
    今天又修改了一下,难道就是为了上“今日头条”?
    --------------------------------------------------------
    没错,我更新了,不顶一下就很少有人会看得到。
     回复 引用 查看   
  49. #58楼 gguowang      2009-01-09 09:30
    没有做Web程序,来凑个热闹,感受一下强人的思想 受益了 谢谢 !
     回复 引用 查看   
  50. #59楼 xjb      2009-01-09 09:40
    老赵写的总给人意外的收获,思考的非常深呀
     回复 引用 查看   
  51. #60楼 IsSuchCloud[未注册用户]2009-01-09 09:43
    还是老赵的方法简便啊
     回复 引用   
  52. #61楼[楼主] Jeffrey Zhao      2009-01-09 09:51
    --引用--------------------------------------------------
    gguowang: 没有做Web程序,来凑个热闹,感受一下强人的思想 受益了 谢谢 !
    --------------------------------------------------------
    这东西其实和Web无关的。事实上以前的反射调用方式总归可以通过类似这样的做法来实现来提高性能。
     回复 引用 查看   
  53. #62楼 江大鱼      2009-01-09 09:59
    <%# FastEval("Content") %>
    这个怎么实现?

     回复 引用 查看   
  54. #63楼 airwolf2026      2009-01-09 10:00
    那这个怎么说,linq to sql 也可以(或者就是用)这个Emit方式来?生成最终的sql?
    学了半年的asp.net 现在总算看懂直接Eval为啥会性能差些啦哈.曾经看'阿不'那个看傻了...

    对啦,请教个问题,目前俺组织打算让俺去做wince开发...(可能主要是c/c++开发),俺现在又觉得c#很好,特别现在的新特性,可以少编码至少可以偷懒,学校的时候主要学c++,知道很多东西都很麻烦.不知道该如何决定...
     回复 引用 查看   
  55. #64楼 韦恩卑鄙      2009-01-09 10:04
    --引用--------------------------------------------------
    Jeffrey Zhao: --引用--------------------------------------------------
    朝晖的.net: 这篇挺不错的,受益匪浅。
    看着这个文章感觉偶要学习的东西还有好多啊。
    感觉老赵进入2009后迅速活跃起来了。
    --------------------------------------------------------
    老赵离江郎才尽永远只有3篇文章。
    --------------------------------------------------------
    老赵你还真实在 不可以把普遍现状说给人听啊


    这叫知无不言言无不尽 你不会说得好听点
     回复 引用 查看   
  56. #65楼 韦恩卑鄙      2009-01-09 10:08
    另外 这么好的方式 感觉这迟早会被封装好内置
    希望老赵能把这个贡献到framework 社区 变成下一代framework的标准eval
    我们就免得理解那么多
     回复 引用 查看   
  57. #66楼 太菜匿了[未注册用户]2009-01-09 10:12
    不好意思,我有一个疑问..
    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
    };
    这些为什么不让数据库去做呢?
     回复 引用   
  58. #67楼 文明的天空      2009-01-09 10:17
    @太菜匿了 可能赵哥只是想教我们一招,这里没有什么意义!
     回复 引用 查看   
  59. #68楼 朝晖的.net      2009-01-09 10:19
    @太菜匿了

    简单点说因为orm的关系,采用oop的设计方法数据库里有张comments和users表,像这种结果
    {
    Title = c.Title,
    Content = c.Content,
    NickName = u.NickName
    };你用数据库做就得做设图,还得mapping一个不干净的对象出来接收结果。
     回复 引用 查看   
  60. #69楼 waterhua[未注册用户]2009-01-09 10:25
    很多3.5的都没用到,因为目前大多数空间也只是到.NET2.0,哎
     回复 引用   
  61. #70楼 韦恩卑鄙      2009-01-09 10:30
    @waterhua
    空间的省下来也是省给别人 不如全占了
    灭哈哈哈
     回复 引用 查看   
  62. #71楼 Caspar Jiong      2009-01-09 10:42
    这么好的文章,怎能不顶,研究研究
     回复 引用 查看   
  63. #72楼[楼主] Jeffrey Zhao      2009-01-09 11:23
    --引用--------------------------------------------------
    江大鱼: <# FastEval("Content") %>
    这个怎么实现?
    --------------------------------------------------------
    没有看文章?
     回复 引用 查看   
  64. #73楼[楼主] Jeffrey Zhao      2009-01-09 11:24
    --引用--------------------------------------------------
    airwolf2026: 那这个怎么说,linq to sql 也可以(或者就是用)这个Emit方式来?生成最终的sql?
    学了半年的asp.net 现在总算看懂直接Eval为啥会性能差些啦哈.曾经看'阿不'那个看傻了...

    对啦,请教个问题,目前俺组织打算让俺去做wince开发...(可能主要是c/c++开发),俺现在又觉得c#很好,特别现在的新特性,可以少编码至少可以偷懒,学校的时候主要学c++,知道很多东西都很麻烦.不知道该如何决定...
    --------------------------------------------------------
    linq to sql当然不是用Emit来做的。
    我也不知道该怎么决定……
     回复 引用 查看   
  65. #74楼[楼主] Jeffrey Zhao      2009-01-09 11:26
    --引用--------------------------------------------------
    韦恩卑鄙: 另外 这么好的方式 感觉这迟早会被封装好内置
    希望老赵能把这个贡献到framework 社区 变成下一代framework的标准eval
    我们就免得理解那么多
    --------------------------------------------------------
    其实这些做法可以用在各种成员中,理论上可以替代大部分的反射调用,的确可以总结一下
     回复 引用 查看   
  66. #75楼[楼主] Jeffrey Zhao      2009-01-09 11:26
    --引用--------------------------------------------------
    韦恩卑鄙:
    老赵你还真实在 不可以把普遍现状说给人听啊

    这叫知无不言言无不尽 你不会说得好听点
    --------------------------------------------------------
    不不,是我自己比较笨而已……
     回复 引用 查看   
  67. #76楼[楼主] Jeffrey Zhao      2009-01-09 11:27
    --引用--------------------------------------------------
    太菜匿了: 不好意思,我有一个疑问..
    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
    };
    这些为什么不让数据库去做呢?
    --------------------------------------------------------
    这是客户端结合多个数据集供显示,和数据库有什么关系。
     回复 引用 查看   
  68. #77楼 古巴[未注册用户]2009-01-09 11:51
    一些细节还没完全理解,不过先问个问题,既然set是通过DynamicExecutor调用的GetSetMethod,那get为什么不类似的调用GetGetMethod呢?
     回复 引用   
  69. #78楼[楼主] Jeffrey Zhao      2009-01-09 11:54
    --引用--------------------------------------------------
    古巴: 一些细节还没完全理解,不过先问个问题,既然set是通过DynamicExecutor调用的GetSetMethod,那get为什么不类似的调用GetGetMethod呢?
    --------------------------------------------------------
    没错,也可以使用GetGetMethod,但是尝试之后会发现性能比直接访问属性略低50%左右。
     回复 引用 查看   
  70. #79楼 hahaer[未注册用户]2009-01-09 11:57
    用Eval优雅吗?个人看起来乱糟糟的。
     回复 引用   
  71. #80楼[楼主] Jeffrey Zhao      2009-01-09 12:01
    @hahaer
    怎么说?
     回复 引用 查看   
  72. #81楼 未上线的蒙蒙[未注册用户]2009-01-09 12:43
    @Jeffrey Zhao
    老赵你明天到长宁路聚合吗?
     回复 引用   
  73. #82楼[楼主] Jeffrey Zhao      2009-01-09 12:46
    --引用--------------------------------------------------
    未上线的蒙蒙: @Jeffrey Zhao
    老赵你明天到长宁路聚合吗?
    --------------------------------------------------------
    晚上有事,下午就用来学习了,呵呵
     回复 引用 查看   
  74. #83楼 viwer[未注册用户]2009-01-09 13:18
    强人啊,用了这么几年都从没想过这个问题,PFPF!
     回复 引用   
  75. #84楼 小鬼00[未注册用户]2009-01-09 14:02
    好久没用控件了.

    反射确实是个好东西,结合具体的应用,使用不同的优化方法,反射的性能并不会成为应用上的瓶颈的.
     回复 引用   
  76. #85楼 天生俪姿      2009-01-09 22:57
    dynamicpropertyaccessor-and-fasteval.html
    我想问一下 这个页面的链接地址是怎么生成出来的?
    英文的名称是你自己指定的吗?在哪里指定的。谢谢老赵。一个很不爽的问题。嘿嘿。
     回复 引用 查看   
  77. #86楼[楼主] Jeffrey Zhao      2009-01-09 23:21
    @天生俪姿
    每篇文章都可以设啊
     回复 引用 查看   
  78. #87楼 overred      2009-01-09 23:21
    顶一下。。可惜不搞web。。。
    Happy Coding阿
     回复 引用 查看   
  79. #88楼[楼主] Jeffrey Zhao      2009-01-09 23:23
    @overred
    和Web没有必然联系
     回复 引用 查看   
  80. #89楼 win[未注册用户]2009-01-10 00:57
    好文章。。。

    顶一下。。。
     回复 引用   
  81. #90楼 装配脑袋      2009-01-12 08:29
    还是编写Client程序好啊,我现在就毫无顾忌地使用反射。。。
     回复 引用 查看   
  82. #91楼[楼主] Jeffrey Zhao      2009-01-12 09:13
    --引用--------------------------------------------------
    装配脑袋: 还是编写Client程序好啊,我现在就毫无顾忌地使用反射。。。
    --------------------------------------------------------
    其实也不会影响WebApp多少的——一回事。
     回复 引用 查看   
  83. #92楼 tensun      2009-01-12 15:58
    又学习了,
    我的第一个watch 居然是: 00:00:07.9052236
    看来楼主机器配置很好。。。
     回复 引用 查看   
  84. #93楼 tensun      2009-01-12 17:05
    不过本人对于Expression 不是很了解,
    对于DynamicPropertyAccessor类第二个构造函数里面的一系列 Expression
    实在是看得有点晕。。。
     回复 引用 查看   
  85. #94楼 JimLiu      2009-01-22 10:41
    支持老赵一下
    我觉得做技术的就是要保持清醒,“性能”和“性能瓶颈”本来就是两个概念,不应该人云亦云
     回复 引用 查看   
  86. #95楼[楼主] Jeffrey Zhao      2009-01-22 10:54
    @JimLiu
    嗯,是。
     回复 引用 查看   
  87. #96楼 魔方网[未注册用户]2009-03-02 13:46
    确实是一片好文章,不得不顶!
    http://www.mofun.cc
     回复 引用   
  88. #97楼 Ivony...      2009-03-11 00:48
    扩展方法不能做到:

    <%# FastEval( xxx ) %>

    吧。

    只能做到:

    <%# this.FastEval( xxx ) %>


    扩展方法毕竟不是自己的方法,是不能省略this的。
     回复 引用 查看   
  89. #98楼[楼主] Jeffrey Zhao      2009-03-11 00:50
    @Ivony...
    没错,如果很在意这点的话,可以把这些辅助方法定义到Page基类中。
     回复 引用 查看   
  90. #99楼 菌哥      2009-04-01 20:51
    100楼,我支持老赵,有志不在年高,真的,我挺佩服老赵的(其实是小赵)
     回复 引用 查看   
  91. #100楼[楼主] Jeffrey Zhao      2009-04-01 23:02
    @菌哥
    多谢支持……
     回复 引用 查看   
  92. #101楼 Sail      2009-04-03 23:44
    老赵 你的字 能不能搞小点,拖来拖去 看着累
     回复 引用 查看   
  93. #102楼 文明的天空      2009-04-06 09:46
    谢谢赵哥!!
     回复 引用 查看   
  94. #103楼 YJJ      2009-09-27 10:47
    看到一半,发现老赵太风趣了
    哈哈
    先回复下再接着看.........
     回复 引用 查看   
  95. #104楼 未登录....[未注册用户]2009-10-17 12:34
    赵老师为什么我通过调用TestDynamic方法比反射花的时间要多的多?您帮我看一下是哪里出了问题...
    protected void Page_Load(object sender, EventArgs e)
    {
    UserInfo userInfo = new UserInfo { UserName = "aaa" };

    PropertyInfo pi = userInfo.GetType().GetProperty("UserName");
    Stopwatch s = new Stopwatch();
    s.Start();
    for (int i = 0; i < 10000; i++)
    {
    var value = pi.GetValue(userInfo, null);
    }
    s.Stop();
    Response.Write(s.Elapsed);
    Stopwatch s1 = new Stopwatch();
    s1.Start();
    for (int i = 0; i < 10000; i++)
    {
    var value = TestDynamic(pi, userInfo);
    }
    s1.Stop();
    Response.Write("<br/>" + s1.Elapsed);
    }
    object TestDynamic(PropertyInfo property, object obj)
    {
    ParameterExpression o = Expression.Parameter(typeof(object), "instance");
    Expression<Func<object, object>> func = Expression.Lambda<Func<object, object>>(
    Expression.Convert(
    Expression.Property(
    Expression.Convert(o, property.ReflectedType), property),
    typeof(object)),o);
    return func.Compile()(obj);
    }
     回复 引用   
  96. #105楼 未登录.....[未注册用户]2009-10-17 17:17
    赵老师,帮我看一下.....谢谢了
     回复 引用   
  97. #106楼[楼主] Jeffrey Zhao      2009-10-17 17:37
    @ 未登录.....
    Compile是最花时间的,正确的做法是Compile一次然后多次调用。
    // 下次发代码,用博客园的代码插入功能吧。
     回复 引用 查看   
  98. #107楼 未登录.....[未注册用户]2009-10-17 18:14
    @Jeffrey Zhao
    太感谢了...就这一句话就行了。。。虽然以前就知道compile最花时间...今天就没想到...
     回复 引用   
  99. #108楼 Everlonely      2010-01-11 15:47
    这个好像不能用于匿名类型,会爆error的
     回复 引用 查看   
  100. #109楼 Everlonely      2010-01-11 15:52
    不好意思,我自己弄错了。测试过了,匿名类型也是OK的。。。
     回复 引用 查看   
  101. 评论共2页: 上一页 1 2 
发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 1371783 phOx8VssyIg=