Hello World
Spiga

一次失败的尝试(上):原来GetCustomAttributes方法每次都返回新的实例

2009-11-10 00:08 by 老赵, 17776 visits

前一段时间我在比较各种URL生成方式性能的时候,其实已经为利用Lambda表达式的做法进行了优化。在优化之前,使用Lambda构建URL的性能比现在的结果还要慢上50%。性能低下的原因,在于每次都使用GetCustomAttributes来获取参数(或其他一些地方)标记的Custom Attribute。这里应该用到了反射,在这种密集调用情形中性能急转直下。

我当时并没有多想,为什么ASP.NET MVC要每次都使用反射来获取一遍——既然Custom Attribute标记都已经静态编译在类型上了,为什么不“缓存”下每个参数所对应的Custom Attribute呢?既然框架没有这么做,那么我就自己进行缓存吧。于是我在辅助方法里针对每个PropertyInfo对象缓存了它所对应的Custom Attribute(其实是CustomModelBinderAttribute的子类),然后每次都可以调用GetBinder方法来获取IModelBinder实例。经过这样的“优化”之后,性能的确有了很大提高。

既然缓存了CustomModelBinderAttribute,那么如系统自带的ModelBinderAttribute那样每次都根据binderType来新建一个IModelBinder对象也没有太大必要了。因为我们几乎所有的IModelBinder都是不依赖上下文的,它的同一个对象可以被多个线程同时调用。如果不是修改了参数状态,它们基本上可以算作是“无副作用”的“纯(pure)函数”。为了减少对象创建或回收时消耗的时间,我写了一个BinderAttribute:

[AttributeUsage(ValidTargets, AllowMultiple = false, Inherited = false)]
public class BinderAttribute : CustomModelBinderAttribute
{
    public BinderAttribute(Type binderType)
        : this(binderType, true) { }

    public BinderAttribute(Type binderType, bool singleton)
    {
        if (binderType == null)
        {
            throw new ArgumentNullException("binderType");
        }

        if (!typeof(IModelBinder).IsAssignableFrom(binderType))
        {
            var message = String.Format("{0} doesn't implement IModelBinder.", binderType.FullName);
            throw new ArgumentException(message, "binderType");
        }

        this.BinderType = binderType;

        var creator = GetBinderCreator(binderType);

        if (singleton)
        {
            var instance = creator();
            this.m_binderGetter = () => instance;
        }
        else
        {
            this.m_binderGetter = creator;
        }
    }

    private static Func<IModelBinder> GetBinderCreator(Type binderType)
    {
        var newExpr = Expression.New(binderType);
        var castExpr = Expression.Convert(newExpr, typeof(IModelBinder));
        var lambdaExpr = Expression.Lambda<Func<IModelBinder>>(castExpr);
        return lambdaExpr.Compile();
    }

    public Type BinderType { get; private set; }

    private readonly Func<IModelBinder> m_binderGetter;

    public override IModelBinder GetBinder()
    {
        return this.m_binderGetter();
    }
}

BinderAttribute的特点在于,它会根据构造函数的singleton参数,来决定是每次都返回同一个还是构造新的实例。在“默认”情况下,singleton为true,这满足大部分情况下“纯”的Model Binder。但如果在万中无一的情况下出现了“只能使用一次”的Model Binder实现,那么您也可以将singleton参数设为false,这样便可以每次都返回新的实例了。此外,即便是返回新的实例,我也使用了表达式树构造的委托来创建新对象,性能比原来的Activator.CreateInstance要高出许多。

但事实上,这种做法并不可行,因为我的“前提”就错了。我的前提是,GetCustomAttributes每次返回的都是同样的对象。换句话说,我以为对于每个Custom Attribute标记,只会创建一个对象,然后多次返回,多次复用。但是经过试验,GetCustomAttributes方法事实上每次都会返回新的对象。这意味着,即便每个BinderAttribute对象各维护一个可复用的IModelBinder对象(如果singleton为true),但如果我们每次都获得新的BinderAttribute对象,这还是达不到singleton的效果。在ASP.NET MVC的场景下,我们的确可以缓存各个CustomModelBinderAttribute(目前也是这么做的),但是这和GetCustomAttributes方法相比还是改变了原有的行为。如果某人在代码里使用了“只能使用一次”的CustomModelBinderAttribute实现,那么我们的“优化”方式从严格意义上来说是不合格的。

因此,MvcPatch并不应该“毫无顾忌”地缓存IModelBinder或CustomModelBinderAttribute对象。那么我们又该怎么办呢?这就是另一个话题了,下次再说吧。

相关文章

Creative Commons License

本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名赵劼(包含链接),具体操作方式可参考此处。如您有任何疑问或者授权方面的协商,请给我留言

Add your comment

67 条回复

  1. 阿阿asd[未注册用户]
    *.*.*.*
    链接

    阿阿asd[未注册用户] 2009-11-10 00:12:00

    肥猪。。

  2. 老赵
    admin
    链接

    老赵 2009-11-10 00:19:00

    @阿阿asd
    还好还好,我现在不算胖了。

  3. winter-cn
    *.*.*.*
    链接

    winter-cn 2009-11-10 00:48:00

    老赵怎么最近盯上构建URL了 不好玩啊

  4. 老赵
    admin
    链接

    老赵 2009-11-10 00:56:00

    @winter-cn
    其实这个和构建URL关系不大啊,你看我都没归到ASP.NET里去。
    过几天就有好玩的了,很好玩,呵呵。

  5. winter-cn
    *.*.*.*
    链接

    winter-cn 2009-11-10 02:31:00

    Jeffrey Zhao:
    @winter-cn
    其实这个和构建URL关系不大啊,你看我都没归到ASP.NET里去。
    过几天就有好玩的了,很好玩,呵呵。


    搬小板凳等看

  6. lovko
    *.*.*.*
    链接

    lovko 2009-11-10 08:43:00

    顶下 没写过mvc 看不懂 哈哈

  7. 老赵
    admin
    链接

    老赵 2009-11-10 09:05:00

    lovko:顶下 没写过mvc 看不懂 哈哈


    我认为其实你只是看到其中的mvc几个字,于是就没有仔细看。
    其实文章只是提到了mvc,这些东西和mvc一点关系也没有。

  8. lovko
    *.*.*.*
    链接

    lovko 2009-11-10 09:10:00

    @Jeffrey Zhao
    回到电脑上再看一遍。手机上代码都乱了,现在重新学习,被揭穿了脸红下,哈哈!

  9. airy
    *.*.*.*
    链接

    airy 2009-11-10 09:13:00

    老赵一般在那里学习呢?MSDN?看书?

  10. 老赵
    admin
    链接

    老赵 2009-11-10 09:34:00

    @airy
    很少看MSDN吧,主要看网络资源,还有自己想。

  11. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-10 09:37:00

    原来只是知道Attribute只是在用到时构建,木想到构建后竟然不缓存。

  12. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-10 09:46:00

    看来还有下文。。。。。不过这个问题不难解决么。

  13. 老赵
    admin
    链接

    老赵 2009-11-10 09:54:00

    @Ivony...
    暂时没有下文,不过会有下文的……我也没想到构建后竟然不缓存。
    这个问题不好解决啊,因为如果某个Attribute真的“只能用一次”的话,我们不能自行缓存它的……

  14. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-10 10:18:00

    Jeffrey Zhao:
    @Ivony...
    暂时没有下文,不过会有下文的……我也没想到构建后竟然不缓存。
    这个问题不好解决啊,因为如果某个Attribute真的“只能用一次”的话,我们不能自行缓存它的……



    Attribute不能缓存,但IModelBinder可以缓存么。

    另外你也可以仅对BinderAttribute进行缓存,而不是CustomModelBinderAttribute不是么?这样别人在使用Attribute的时候就要自行注意了,如果明确使用你的BinderAttribute的话,就应该知道这是很明确会被缓存的。

  15. 老赵
    admin
    链接

    老赵 2009-11-10 10:20:00

    @Ivony...
    理论上ModelBinder也可能是“只能用一次”的……话说我最后使用的的确是你说得“仅缓存BinderAttribute”。
    事实上,我计划缓存标记了我提供的ReusableAttribute的ModelBinder或CustomModelBinderAttribute。

  16. heros
    *.*.*.*
    链接

    heros 2009-11-10 10:36:00

    既然是GetCustomAttribute的问题,为什么不另提供一个 GetSingletonCustomAttribute呢.想用什么用什么.不知道是否可行.

  17. 老赵
    admin
    链接

    老赵 2009-11-10 10:42:00

    @heros
    你这个就是手动缓存啊。
    我不是不知道怎么缓存,而是缓存的话,就改变GetCustomAttribute的原有功能了。
    可能某些Attribute会依赖“每次创建新对象”这点,因此理论上不能进行手动的缓存。

  18. heros
    *.*.*.*
    链接

    heros 2009-11-10 10:52:00

    现在是就不能绕开GetCustomAttribute方法?先前理解有误.

  19. 老赵
    admin
    链接

    老赵 2009-11-10 10:53:00

    @heros
    可以绕开,也必须绕开,否则没法提升性能啊。
    但是不能靠缓存,因为缓存会改变行为,呵呵。

  20. heros
    *.*.*.*
    链接

    heros 2009-11-10 11:09:00

    这就有点不理解了,必须绕开GetCustomAttribute,那GetCustomAttribute每次返回是单一对象还是新对象都没关系了.

  21. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-10 11:17:00

    heros:这就有点不理解了,必须绕开GetCustomAttribute,那GetCustomAttribute每次返回是单一对象还是新对象都没关系了.




    是不能擅自缓存的问题,我们假定有一个这样的CustomModelBinderAttribute,他的GetBinder方法是这样写的:
    public IModelBinder GetBinder()
    {
      //...
      Dispose( true );
    }
    

    那么很显然的,缓存这个Attribute是不行的。

    IModelBinder同理。


    当然这是极端的例子,没有什么类型会这样写,但可能存在我们不知道的情况。

  22. heros
    *.*.*.*
    链接

    heros 2009-11-10 12:27:00

    擅自缓存有问题.先前认为GetCustomAttribute有问题那就替换掉,现在来看,单一对象的思路可能就是不合适的.GetCustomAttribute返回是单一的还是一个新的都没关系.主要矛盾是性能的问题.

  23. 老赵
    admin
    链接

    老赵 2009-11-10 12:55:00

    @heros
    是的,因为GetCustomAttribute有性能问题,所以缓存对象,避免每次都调用GetCustomAttribute。
    然而,既然GetCustomAttribute的语义每次都返回新的对象,这样我们就不能擅自缓存了。
    要提高性能,还得用保持GetCustomAttribute语义——其实就是创建新对象的方式进行……

  24. heros
    *.*.*.*
    链接

    heros 2009-11-10 13:57:00

    找到了TypeDescriptor , PropertyDescriptor 几个类,也可以用来获取Attribute,性能上取100万次Attribute几本上时间是GetCustomAttribute的十分之一.
    不过再一测试,原来它就是GetCustomAttribute的单件版.

  25. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-10 14:03:00

    heros:
    找到了TypeDescriptor , PropertyDescriptor 几个类,也可以用来获取Attribute,性能上取100万次Attribute几本上时间是GetCustomAttribute的十分之一.
    不过再一测试,原来它就是GetCustomAttribute的单件版.




    这几个类,看起来主要是给组件(事实上适用于任何类型)伪造属性和方法的。其实微软这里的设计很古怪,这几个类型主要是设计器来使用(还有Binder),设计器透过这几个类型来获取组件/控件类型的元数据。然后组件/控件可以实现接口来控制这几个类型的行为,伪造自己的元数据(当然直接反射获得的元数据是伪造不了的)。

    最典型的例子就是DataRowView,这个东西通过伪造元数据的手段谎称自己有些什么属性,使得数据绑定的时候可以把列名直接当作DataRowView的属性来绑定。

  26. heros
    *.*.*.*
    链接

    heros 2009-11-10 14:16:00

    @Ivony...
    TypeDescriptor 还是解决不了这里的问题.TypeDescriptor似乎也是自已缓存了,多次getattribute,也只调用了一次自定义attribute的构造函数.

  27. heros
    *.*.*.*
    链接

    heros 2009-11-10 14:24:00

    又细想了一下,觉得缓存也不是不可以使用.
    Attribute已经静态的绑定在了类型信息上,一般情况下其实getattribute出来后缓存了未尝不可.如果被缓存的对象可以自已告诉getattribute方法现在它是否可用,那样getattribute方法可以决定是否需要创建新的对象来替换缓存数据,极端情况的问题也就不存在了.

  28. 老赵
    admin
    链接

    老赵 2009-11-10 14:27:00

    @heros
    其实,我还真没有遇到过不能缓存的Attribute,现在算是个性能优化的纯技术问题了,呵呵。
    只可惜现在GetCustomAttribute的行为是不可以修改的……

  29. 老赵
    admin
    链接

    老赵 2009-11-10 14:39:00

    @heros
    闲得蛋疼的兄弟们可以用Reflector看看类的方下面这个方法:

    namespace System.Reflection
    {
        internal class CustomAttribute
        {
            internal static unsafe object[] GetCustomAttributes(Module decoratedModule, int decoratedMetadataToken, int pcaCount, RuntimeType attributeFilterType, bool mustBeInheritable, IList derivedAttributes) { ... }
        }
    }
    这个方法就是.NET Framework里的实现。
    当然,我没仔细看,估计仔细看了也不容易看懂,不过发现最后的确是使用Activator.CreateInstance(Type)或Activator.CreateInstance(Type, object[])创建对象的。
    也就是说,的确是很单纯的使用反射,每次都创建一下。

  30. heros
    *.*.*.*
    链接

    heros 2009-11-10 14:39:00

    原先的想法是,构造IAvailable接口,要求需要被缓存的Attribute实现它.同时不再直接使用GetCustomAttribute方法,而是创建一个带有缓存的新的GetCachableCustomAttribute,它要能够根据Available的结果来决定是否缓存或是否需要更新缓存.

  31. 老赵
    admin
    链接

    老赵 2009-11-10 14:43:00

    @heros
    的确是可以这样的,所以我现在使用标记[Reusable]的方式来指定是否可以重用。
    只要可以缓存的话,这个性能优化是容易的。难点在于,如果不能重用的话……有没有办法可以提高每次调用GetCustomAttribute的性能。

  32. heros
    *.*.*.*
    链接

    heros 2009-11-10 14:43:00

    @Jeffrey Zhao
    估计没几个能像我这样工作时候能闲的很.

  33. 老赵
    admin
    链接

    老赵 2009-11-10 14:46:00

    @heros
    我现在有个保底的做法,大致思路是真正读取“元数据”,二进制方式的读静态数据,获得究竟有哪些Attribute标记,以及使用什么参数进行构建。
    这个虽然不是一个可以替代GetCustomAttribute的实现,但是的确可以在基本上各个情况下使用。
    因为,其实经过了静态编译之后,每次GetCustomAttribute的行为都是确定的。我们可以缓存这些元数据,每次都自己构造一下就行了。

  34. heros
    *.*.*.*
    链接

    heros 2009-11-10 14:56:00

    或许不必如此,可以在第一次GetCustomAttribute时将得到的信息做为原型缓存起来,以后由原型来构建.

  35. 老赵
    admin
    链接

    老赵 2009-11-10 15:00:00

    @heros
    通过GetCustomAttribute得到的信息是不完整的,我们只能知道创建了哪些对象,但是已经没法知道这些对象是怎么创建的了。

  36. heros
    *.*.*.*
    链接

    heros 2009-11-10 15:04:00

    那用CustomAttributeData.GetCustomAttributes(……);好了.

  37. 老赵
    admin
    链接

    老赵 2009-11-10 15:08:00

    @heros
    干啊,我怎么就才知道这个方法,那么问题就好办多了……

  38. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-10 15:32:00

    哈,我也学到了。。。。。

  39. 老赵
    admin
    链接

    老赵 2009-11-10 15:35:00

    @Ivony...
    是啊……干了好多年.NET没想到还有那么多不知道的……
    我们写本书叫做《你不知道的.NET》吧。

  40. heros
    *.*.*.*
    链接

    heros 2009-11-10 21:54:00

    突然想起来的,在第一次GetCustomAttribute时将得到的对象做为原型缓存起来,既然是原型对象,它自己就可以Clone新对象,就不用关心如何创建了吧?我是说如果让Attribute实现IClonable的话.

  41. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-10 22:15:00

    heros:突然想起来的,在第一次GetCustomAttribute时将得到的对象做为原型缓存起来,既然是原型对象,它自己就可以Clone新对象,就不用关心如何创建了吧?我是说如果让Attribute实现IClonable的话.




    老赵担心的是性能的问题,呵呵,因为创建对象的性能损耗比较头疼。。。老实说我也没想到这么慢。。。。。

  42. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-10 22:21:00

    Jeffrey Zhao:
    @Ivony...
    是啊……干了好多年.NET没想到还有那么多不知道的……
    我们写本书叫做《你不知道的.NET》吧。




    其实不用说深了啊,一些很基础的东东,其实我到现在都没弄通透。

    例如数组,数组类型是CLR编造出来的。int[]并没有自己的GUID,也不是一个泛型类型。是继承于Array的一个编造出来的特殊类型,这种类型并不如其他类型具备元数据,而是CLR根据int直接编造。

    例如接口,显式接口实现成员的可访问性,也有很多人不知道吧,由于显式接口实现只能透过接口类型来访问,所以显示接口实现的成员可见性由接口确定,简单的说,如果你访问不到那个接口类型,即使是派生类都没有办法访问基类的某个显式接口实现。传言VB显式实现接口的时候,连方法名称都不必相同,只要参数和返回值类型相同即可。不过C#的显式接口实现,也不存在什么名称,直接用名称在本类内都是无法访问。

    和数组一样,指针类型也是一种特殊的类型。。。。。


    但是,诸如数组和指针类型CLR具体如何编造,还有没有类似的编造类型?接口的显示实现为什么是newslot virtual final,还override接口成员,这意味着什么?。。。。。好多搞不清的问题啊。。。。

  43. heros
    *.*.*.*
    链接

    heros 2009-11-10 22:32:00

    @Ivony...
    我知道啊,上面那么多评论说的就是如何解决性能啊。GetCustomAttribute少量的调用没有问题,关键是密集的调用。

    上面说到CustomAttributeData.GetCustomAttributes(……)是为了能够获取Attribute信息,帮助了解Attribute的构造信息以便手动创建。

    我是觉得手动创建有点麻烦了,程序里面加的自定义Attribute能有多少?如果这个Attribute本身可以是个原型对象,直接提供复制功能,那就在第一次GetCustomAttribute得到它的时候缓存起来,但并不直接使用这个缓存的对象,而是使用它的克隆版,这样也无需了解这个对象是如何创建的了。性能上肯定也要比每次都GetCustomAttribute来的好。

  44. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-10 22:36:00

    heros:
    @Ivony...
    我知道啊,上面那么多评论说的就是如何解决性能啊。GetCustomAttribute少量的调用没有问题,关键是密集的调用。

    上面说到CustomAttributeData.GetCustomAttributes(……)是为了能够获取Attribute信息,帮助了解Attribute的构造信息以便手动创建。

    我是觉得手动创建有点麻烦了,程序里面加的自定义Attribute能有多少?如果这个Attribute本身可以是个原型对象,直接提供复制功能,那就在第一次GetCustomAttribute得到它的时候缓存起来,但并不直接使用这个缓存的对象,而是使用它的克隆版,这样也无需了解这个对象是如何创建的了。性能上肯定也要比每次都GetCustomAttribute来的好。




    你复制的时候总不能是memcopy吧,这不还是有对象创建的损耗?或者你是说这会比GetCustomAttribute性能更好?

    不过我觉得这有点偏,如果要这个Attribute实现个什么IClonable接口,还不如直接实现个标记接口告诉别人自己可以享元不就得了?

  45. 老赵
    admin
    链接

    老赵 2009-11-10 22:42:00

    heros:突然想起来的,在第一次GetCustomAttribute时将得到的对象做为原型缓存起来,既然是原型对象,它自己就可以Clone新对象,就不用关心如何创建了吧?我是说如果让Attribute实现IClonable的话.


    这不还是对Attribute做要求了么,呵呵。

  46. 老赵
    admin
    链接

    老赵 2009-11-10 22:43:00

    Ivony...:
    老赵担心的是性能的问题,呵呵,因为创建对象的性能损耗比较头疼。。。老实说我也没想到这么慢。。。。。


    对象创建性能不是关键,这里的大头是反射,我测试过,纯使用Activator.CreateInstance大约占GetCustomAttribute的1/8。

  47. 老赵
    admin
    链接

    老赵 2009-11-10 22:46:00

    Ivony...:
    不过我觉得这有点偏,如果要这个Attribute实现个什么IClonable接口,还不如直接实现个标记接口告诉别人自己可以享元不就得了?


    是的,我的看法和你一样。就是说,我们可以根据我们对Attribute的元信息来操作它,但对Attribute本身作要求就不太好了。
    我说的,读取Attribute标记然后手动构造Attribute对象,这个是可以推广之的通用方式,不需要对Attribute有任何特别要求,呵呵。

  48. heros
    *.*.*.*
    链接

    heros 2009-11-10 23:02:00

    @Jeffrey Zhao
    我是说如果让Attribute实现IClonable的话,这也只是假设的前提。让每个自定义的Attribute都实现Icloneable当然是很糟糕,太重了。假如这么做的话,是不需要了解如何构建的,假如的。
    还是想看Jeffrey Zhao原先保底方案的实现。用CustomAttributeData就不精采了。
    Ivony你的方案是什么?

  49. 老赵
    admin
    链接

    老赵 2009-11-10 23:09:00

    @heros
    呵呵,CustomAttributeData已经很精彩了,让我先总结一篇,然后再写原先的保底方案,呵呵。

  50. heros
    *.*.*.*
    链接

    heros 2009-11-10 23:23:00

    Ivony一个劲的责问,殊不知我已经把高招阴招全摆出来了。解决问题嘛,有招就出,越多越好,再挑好的用呗。阿贵说的好,该困觉了。期待保底方案.

  51. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-11 00:06:00

    heros:Ivony一个劲的责问,殊不知我已经把高招阴招全摆出来了。解决问题嘛,有招就出,越多越好,再挑好的用呗。阿贵说的好,该困觉了。期待保底方案.




    不是责问,,,,呵呵,,,,是不理解。。。。

    不过现在理解了。

    刚才没想通一件事,我们为什么一定要创建Attribute的实例,CustomAttributeData的信息还不够充分么?后来仔细一想才明白,这个Attribute有个GetBinder方法呢,不构建实例就不可能调用这个方法。

    这样看起来,问题似乎还是没有解决。因为我们必须创建Attribute实例,即使我们是通过CustomAttributeData信息自行创建,不见得会比GetCustomAttributes好多少,毕竟我们顶多知道Attribute实例的基类型。情况似乎还是没有改观,不透过反射,我们还是没有办法构建一个完整的Attribute实例出来。我们顶多能针对某些Attribute做性能的优化,却不能对所有未知的Attribute做性能优化。尽管我们可以透过Emit缓存操作,减少反射带来的性能损耗,但这样仍然稍显麻烦。

  52. Ivony...
    *.*.*.*
    链接

    Ivony... 2009-11-11 00:23:00

    要我说解决方案的话,我会觉得我们没有必要去顾及太多,毕竟具体问题具体分析,不太可能有一种没有任何副作用的方案可以直接解决性能问题,对于任何Attribute都适用。

    就LZ的这个例子来说,我会觉得自己写一个BinderAttribute,然后自己的框架默认对这个Attribute的实例进行缓存就可以了。如果有派生自这个类型的Attribute要求框架不对实例进行缓存,再利用其他的方式来明确这一点。

    当然,我们也可以自定义一个BinderAttribute,抛弃这个所谓的GetBinder方法,纯粹通过配置来表明自己的意图。这样,我们根本就无需创建Attribute实例,获得最好的性能。

    诚然,直接透过CustomAttributeData来Emit(或构建表达式树)创建实例的逻辑,然后将这个逻辑缓存下来,当下次遇到类似的CustomAttributeData时直接使用这个逻辑来创建实例也不失为一种解决方案。但个人感觉很麻烦,,,,

  53. 老赵
    admin
    链接

    老赵 2009-11-11 00:28:00

    @Ivony...
    无论怎么做,肯定比GetCustomAttribute要麻烦的,呵呵,其实我们现在就是想尽办法来优化性能。
    其实我想,既然有CustomAttributeData了,应该可以写出一个通用的高性能的GetCustomAttributeEx来替代原有的实现。
    因为CustomAttributeData是不会变的,所以有哪些个标记其实都是静态的,可以缓存下来。
    缓存下来之后,构造对象其实可以通过Emit来加快性能。
    而且,事实上经过我的简单测试,GetCustomAttribute方法和直接Activator.CreateIntance相比,耗时大约是8比1。
    这么看来,如果我们可以缓存CustomAttributeData的话,性能就已经可以提高不少了。

  54. 韦恩卑鄙 alias:v-zhewg
    *.*.*.*
    链接

    韦恩卑鄙 alias:v-zhewg 2009-11-11 00:56:00

    @Jeffrey Zhao
    话说这次这两个问题我都遇到过-0- 可耻的被我自己就饭吃了

  55. 老赵
    admin
    链接

    老赵 2009-11-11 01:03:00

    @韦恩卑鄙 alias:v-zhewg
    那么现在再吐点出来吧……

  56. 老赵
    admin
    链接

    老赵 2009-11-11 01:03:00

    @Ivony...
    你的想法和我几乎完全一样,所以最终我就缓存带有[Runnable]标记的CustomModelBinderAttribute和IModelBinder对象了……

  57. 韦恩卑鄙 alias:v-zhewg
    *.*.*.*
    链接

    韦恩卑鄙 alias:v-zhewg 2009-11-11 01:09:00

    Jeffrey Zhao:
    @韦恩卑鄙 alias:v-zhewg
    那么现在再吐点出来吧……


    我先消化消化你们回的内容 ~~~~ 嗝儿

  58. 老赵
    admin
    链接

    老赵 2009-11-11 01:17:00

    @韦恩卑鄙 alias:v-zhewg
    慢慢消化,祝好胃口,俺先找吴妈困觉去了……

  59. 韦恩卑鄙 alias:v-zhewg
    *.*.*.*
    链接

    韦恩卑鄙 alias:v-zhewg 2009-11-11 01:22:00

    赵老爷子你好睡 我等等也去梦里看杀头

  60. 韦恩卑鄙 alias:v-zhewg
    *.*.*.*
    链接

    韦恩卑鄙 alias:v-zhewg 2009-11-11 01:47:00



    我不太清楚在mvc里面你们的用况是什么 我一向都是不在attribute 里面放上下文、非静态字段。 一般有限集合我都建立相应个数的Attribute,因为Attribute.IsDefined 速度快啊
    >_<



    另外 如果attribute里面没有任何成员上下文 只有静态的字段 是不是也能减少创建的消耗呢(对IClonable来说 按照这个原则建立的类memcopy几乎不需要时间)


    比如说

    Interface IMyAttribute 
    {
       int P1{get;set;}
    }
    
    Class MyAttribute1:Attribute,IMyAttribute
    {
        static int _P1 =1;
    
    int P1{get{return _P1;} set{_P1=value}}
    }
    
    Class MyAttribute2:Attribute,IMyAttribute
    {
        static int _P1 =2;
    
        int P1{get{return _P1;} set{_P1=value}}
    }
    
    Class MyAttribute3:Attribute,IMyAttribute
    {
        static int _P1 =3;
    
        int P1{get{return _P1;} set{_P1=value}}
    }
    
    

    这样我只需要判断某一个类型是否IsDefined 就可以知道里面的数值了吧

    ModelBinder不熟悉 估计全都说偏了 -0-

  61. 韦恩卑鄙 alias:v-zhewg
    *.*.*.*
    链接

    韦恩卑鄙 alias:v-zhewg 2009-11-11 01:54:00

    又看了一遍
    为什么
    private readonly Func<IModelBinder> m_binderGetter;


    不能是static的呢?

    还是搞不懂=0=

  62. 韦恩卑鄙 alias:v-zhewg
    *.*.*.*
    链接

    韦恩卑鄙 alias:v-zhewg 2009-11-11 01:54:00

    梦里看杀头去了 一头问号。。。

  63. 老赵
    admin
    链接

    老赵 2009-11-11 09:47:00

    @韦恩卑鄙 alias:v-zhewg
    ModelBinderAttribute的作用是标记某个参数应该使用哪个IModelBinder类型作为转化。
    IsDefined只能判断“有没有定义”,但是我们现在还需要知道具体是哪个IModelBinder对象。
    所以我们要实例化ModelBinderAttribute对象,然后调用它的GetBinder方法。

  64. 老赵
    admin
    链接

    老赵 2009-11-11 09:48:00

    @韦恩卑鄙 alias:v-zhewg
    肯定不能是static的啊,因为可能出现两个BinderAttribute标记:
    [Binder(typeof(StringBinder))]
    [Binder(typeof(ArticleBinder))]
    如果用static不就挂掉了……

  65. 韦恩卑鄙 alias:v-zhewg
    *.*.*.*
    链接

    韦恩卑鄙 alias:v-zhewg 2009-11-11 09:54:00

    @Jeffrey Zhao
    >_< 我会用山寨写法写成


    abstract class Binder
    {
    abstract public TheType {get};
    
    }
    
    Class SomeStringBinder:Binder
    {
     static Type _TheType=typeof(StringBinder));
      override public TheType {get{return _TheType;}}
    }
    
    Class SomeArticleBinder:Binder
    {
      static Type _TheType=typeof(ArticleBinder));
      override public TheType {get{return _TheType;}}
    }
    
    

    [SomeStringBinder()]
    [SomeArticleBinder()]

    我的意思是 在这种反射造成巨大性能浪费的场景下 与其没有预期的生成实例来检查 可能不如把可能性考虑进去挨个试验呢 :(

    只是随便说说 目前mvc还没学太懂 binder说的肯定驴唇不对马嘴 >_<

  66. 老赵
    admin
    链接

    老赵 2009-11-11 10:02:00

    @韦恩卑鄙 alias:v-zhewg
    那不还是要实例化SomeStringBinderAttribute和SomeArticleBinderAttribute吗?
    还有就是,如果为每个Binder再写一个Attribute也麻烦了……

  67. 韦恩卑鄙 alias:v-zhewg
    *.*.*.*
    链接

    韦恩卑鄙 alias:v-zhewg 2009-11-11 10:03:00

    Jeffrey Zhao:
    @韦恩卑鄙 alias:v-zhewg
    那不还是要实例化SomeStringBinderAttribute和SomeArticleBinderAttribute吗?
    还有就是,如果为每个Binder再写一个Attribute也麻烦了……


    静态成员 按理不是可以直接根据Type取得么 (按理说阿—·#*!)

    想办法走个后门吧

    根据 SomeStringBinder的Type 直接取出来_TheType


    好吧我承认我越来越糊涂了

发表回复

登录 / 登录并记住我 ,登陆后便可删除或修改已发表的评论 (请注意保留评论内容)

昵称:(必填)

邮箱:(必填,仅用于Gavatar

主页:(可选)

评论内容(大于5个字符):

  1. Your Name yyyy-MM-dd HH:mm:ss

使用Live Messenger联系我