使用Attribute+Reflection来提高代码重用

这篇文章两个目的,一是开阔设计的思路,二是实例代码可以拿来就用。

设计的思路来源于《Effective c#》第一版Item 24: 优先使用声明式编程而不是命令式编程。特别 的地方是,希望提供多个属性的默认排序,而不仅仅只根据一个属性,另外一点是,优先调用对象属性 实现了的IComparable<T>接口,如果没有实现接口,才调用IComparable进行比较。排序类实现 泛型,得到类型安全。

总的思路:Attribute用来装饰我们想要获取元数据的类,使用Reflection来提取元数据,根据提取 到的元数据实现一些和对象无关的组件功能。

那么,这个例子要实现的效果是用Attribute装饰类对象,设置该对象的默认排序属性,排序的时候 ,根据这些默认排序来进行排序。

[DefaultSort(new string[] {"ID", "Name"})]
class SortData
{
    public int ID { get; set; }

    public string Name { get; set; }

    public string Value { get; set; }

    public override string ToString()
    {
        return String.Format("ID:{0},Name:{1},Value:{2}", ID, Name, Value);
    }
}

对于SortData对象来说,我们希望根据它的ID来排序,如果ID相等,再根据Name属性来排序。像它 的名字暗示的一样,这是默认的行为,不需要我们实现SortData的IComparable<SortData>接口 ,将来要改变排序规则,只要修改DefaultSort中属性名称数组的内容就够了,很方便。

原书中记录的DefaultAttribute只能根据一个属性名称来排序,不够实用,希望它像下面的类一样 ,能记录多个属性的名称。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple=false)]
    public class DefaultSortAttribute : System.Attribute
    {
        private string[] m_propNames;

        public string[] PropNames
        {
            get { return m_propNames; }
            set { m_propNames = value; }
        }

        public DefaultSortAttribute(string propName)
        {
            m_propNames = new string[] { propName };
        }

        public DefaultSortAttribute(string[] propNames)
        {
            m_propNames = propNames;
        }
    }

注意仍然保留了只希望拿一个属性来排序的构造函数,对类进行装饰时,往类上面放[DefaultSort (new string[] {"ID", "Name"})] 和[DefaultSort("ID")]类似的声 明就够了。

既然使用Attribute装饰了类,就要知道这样的元数据,下面需要采用Reflection读到要排序的默认 属性名,相对于原书中的改进是,使用泛型和优先使用属性的IComparable<T>接口来比较排序。

using System;
using System.Linq;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Reflection;

namespace ProJKY.Extensions
{
    public class DefaultSortComparer<T> : IComparer, IComparer<T>
    {
        private readonly PropertyDescriptor[] m_sortProps;
        private readonly bool m_reverse = false;
        private readonly bool m_valueType = false;

        public DefaultSortComparer() :
            this(false)
        { }

        public DefaultSortComparer(bool reverse)
        {
            m_reverse = reverse;
            Type t = typeof(T);
            m_valueType = t.IsValueType;

            object[] a = t.GetCustomAttributes(typeof(DefaultSortAttribute), false);

            // 强制检查,不支持没有用DefaultSortAttribute装饰的类
            if (a.Length != 1)
                throw new NotSupportedException(t.Name);

            DefaultSortAttribute sortName = a[0] as DefaultSortAttribute;

            string[] propNames = sortName.PropNames;

            m_sortProps = new PropertyDescriptor[propNames.Length];

            PropertyDescriptorCollection props = TypeDescriptor.GetProperties(t);

            for (int i = 0; i < propNames.Length; i++){
                foreach (PropertyDescriptor p in props){
                    if (p.Name == propNames[i]){
                    m_sortProps[i] = p;
                    break;
                    }
                }
            }
        }

        int IComparer.Compare(object left, object right)
        {
            if (HasNull(left, right) == true)
            {
                int nullCompare = CompareWithNull(left, right);

                return m_reverse ? -nullCompare : nullCompare;
            }

            if (left.GetType() != right.GetType())
                throw new ArgumentException("left and right not match.");

            if (typeof(T).IsAssignableFrom(left.GetType()) == false)
                throw new ArgumentException("type not compatible.");

            return Compare((T)left, (T)right);
        }

        public int Compare(T x, T y)
        {
            if (m_valueType == false && HasNull(x, y) == true){
                int nullCompare = CompareWithNull(x, y);
                return m_reverse ? -nullCompare : nullCompare;
            }

            foreach (var prop in m_sortProps){
                object xValue = prop.GetValue(x);
                object yValue = prop.GetValue(y);

                if (HasNull(xValue, yValue) == true){
                    int nullCompare = CompareWithNull(xValue, yValue);

                    return m_reverse ? -nullCompare : nullCompare;
                }

                Type propType = xValue.GetType();

                // 优先使用IComaprable<T>接口
                if (typeof(IComparable<>).MakeGenericType(propType).IsAssignableFrom(propType))
                {
                    MethodInfo methodInfo = propType.GetMethods().FirstOrDefault(method => method.Name == "CompareTo"
                        && method.GetParameters().Length == 1
                        && method.GetParameters()[0].ParameterType == propType);

                    int gretValue = (int)methodInfo.Invoke(xValue, new object[] { yValue });

                    if (gretValue == 0) continue;

                    return m_reverse ? -gretValue : gretValue;
                }

                IComparable xNonGeneric = xValue as IComparable;
                IComparable yNonGeneric = yValue as IComparable;

                if (xNonGeneric == null)
                    throw new ArgumentException("Property " + prop.Name + " is not comparable.");

                int retValue = xNonGeneric.CompareTo(yValue);

                if (retValue == 0) continue;

                return m_reverse ? -retValue : retValue;
            }

            return 0;
        }

        int CompareWithNull(object left, object right)
        {
            if ((left == null) && (right == null))
                return 0;

            if (left == null)
                return -1;

            return 1;
        }

        bool HasNull(object left, object right)
        {
            if (left == null || right == null)
                return true;
            return false;
        }
    }
}

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索string
, object
, 排序
, return
, 属性
, public
selec,t改变readonly属性
c 代码设计与重用、代码重用、代码重用性、代码的重用性、c代码设计与重用 pdf,以便于您获取更多的相关知识。

时间: 2024-11-04 08:32:04

使用Attribute+Reflection来提高代码重用的相关文章

持久化模式,第 2 部分: 提高代码重用和改进性能

简介 本系列包含两篇文章,第 1 部分 讨论了 Hibernate 和其他对象-关系映射(ORM)工具的几个基本最佳实践.通过使用通用基领域类 和接口.集中的审计和泛型数据访问对象(泛型 DAO),应用程序可以建立更紧凑且可维护的领域模型和持久化层. 通过应用 第 1 部分 中的概念,可以提供新的代码重用机会.在这个部分中,我们首先讨论如何使用 Hibernate 和多态性在领域模型 中集成行为.接下来,继续 第 1 部分 对泛型 DAO 的讨论.在应用程序中集成和使用泛型 DAO 之后,您可能

利用Java反射(Reflection) 机制提高代码的行覆盖率

在本文中,您将看到如何通过使用反射机制,在外部直接对目标类中的不可访问成员进行测试,以提高被测代码数量:以及通过修改 Cobertura 源码,使其支持通过正则表达式来过滤不需要进行单元测试的代码,以降低代码总数.代码覆盖率的提高,减少了单元测试过程中未被覆盖的代码数量,降低了http://www.aliyun.com/zixun/aggregation/7155.html">开发人员编写或修改单元测试用例的时间成本,从而提高了整个单元测试的效率. 单元测试是软件开发过程中重要的质量保证环

javascript 命名空间以提高代码重用性_javascript技巧

当在同一个网页里引入10多个js文件之后, 各js中的同名函数就很容易冲突了. 比如xxx库里写了个addCssStyle方法, yyy类库里也写了个addCssStyle方法, 而这两个方法的具体实现又有一定差别. 那么同时引用这两个组件的时候,函数冲突之后导致页面效果发生变化, 调试和修改都是非常痛苦的,如果为了避免冲突, 而放弃引用一些优秀的组件,那更是让人郁闷的事情. 为此,在封装javascript组件库的时候,请使用命名空间来避免冲突. 将所有的方法和变量都要按包名类名的方式来写.

BCB中用Sender参数实现代码重用

面向对象的编程工具的特点之一就是要提高代码重用性(Reuse),宝兰的BCB当然可以实现这一功能.我们都知道,在BCB中,大部分程序代码都直接或间接的对应着一个事件,此程序称为事件处理句柄,它实际上就是一个过程.从应用程序的工程到窗口.组件和程序,BCB强调的是其开发过程中每一层次的重用性,可以充分利用已编写过的代码来减少工作量,更会使你的程序变得优美.代码段间的共享都跟发生该事件的控件有关有关,需要根据控件类型做出相应的处理,这时就要用到Sender参数. 每个函数的开头都有形如: void

最大限制地提高代码的可重用性

    重用是一种神话,这似乎正在日渐成为编程人员的一种共识.然而,重用可能难以实现,因为传统面向对象编程方法在可重用性方面存在一些不足.本技巧说明了组成支持重用的一种不同方法的三个步骤. 第一步:将功能移出类实例方法由于类继承机制缺乏精确性,因此对于代码重用来说它并不是一种最理想的机制.也就是说,如果您要重用某个类的单个方法,就必须继承该类的其他方法以及数据成员.这种累赘不必要地将要重用此方法的代码复杂化了.继承类对其父类的依赖性引入了额外的复杂性:对父类的更改会影响子类:当更改父类或子类中的

最大限制地提高代码的可重用性,克服传统面向对象编程方法在可重用性方面的不足

编程|对象     重用是一种神话,这似乎正在日渐成为编程人员的一种共识.然而,重用可能难以实现,因为传统面向对象编程方法在可重用性方面存在一些不足.本技巧说明了组成支持重用的一种不同方法的三个步骤. 第一步:将功能移出类实例方法由于类继承机制缺乏精确性,因此对于代码重用来说它并不是一种最理想的机制.也就是说,如果您要重用某个类的单个方法,就必须继承该类的其他方法以及数据成员.这种累赘不必要地将要重用此方法的代码复杂化了.继承类对其父类的依赖性引入了额外的复杂性:对父类的更改会影响子类:当更改父

提高Java代码重用性的三个措施

措施一:改写类的实例方法 通过类继承实现代码重用不是精确的代码重用技术,因此它并不是最理想的 代码重用机制.换句话说,如果不继承整个类的所有方法和数据成员,我们无法 重用该类里面的单个方法.继承总是带来一些多余的方法和数据成员,它们总是 使得重用类里面某个方法的代码复杂化.另外,派生类对父类的依赖关系也使得 代码进一步复杂化:对父类的改动可能影响子类;修改父类或者子类中的任意一 个类时,我们很难记得哪一个方法被子类覆盖.哪一个方法没有被子类覆盖;最 后,子类中的覆盖方法是否要调用父类中的对应方法

11步提高代码质量和整体工作效率

  这篇文章要介绍的,是我作为专业程序员这些年来学到的能真正提高我的代码质量和整体工作效率的11件事情. 1. 永远不要复制代码 不惜任何代价避免重复的代码.如果一个常用的代码片段出现在了程序中的几个不同地方,重构它,把它放到一个自己的函数里.重复的代码会导致你的同事 在读你的代码时产生困惑.而重复的代码如果在一个地方修改,在另外一个地方忘记修改,就会产生到处是bug,它还会使你的代码体积变得臃肿.现代的编程语 言提供了很好的方法来解决这些问题,例如,下面这个问题在以前很难解决,而如今使用lam

MVC 设计模式带来更好的软件结构和代码重用

设计 模型-视图-控制器(MVC)是Xerox PARC在八十年代为编程语言Smalltalk-80发明的一种软件设计模式,至今已被广泛使用.最近几年被推荐为Sun公司J2EE平台的设计模式,并且受到越来越多的使用 ColdFusion 和 PHP 的开发者的欢迎.模型-视图-控制器模式是一个有用的工具箱,它有很多好处,但也有一些缺点. MVC如何工作 MVC是一个设计模式,它强制性的使应用程序的输入.处理和输出分开.使用MVC应用程序被分成三个核心部件:模型.视图.控制器.它们各自处理自己的任