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

设计的思路来源于《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 != )
throw new NotSupportedException(t.Name); DefaultSortAttribute sortName = a[] as DefaultSortAttribute; string[] propNames = sortName.PropNames; m_sortProps = new PropertyDescriptor[propNames.Length]; PropertyDescriptorCollection props = TypeDescriptor.GetProperties(t); for (int i = ; 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 ==
&& method.GetParameters()[].ParameterType == propType); int gretValue = (int)methodInfo.Invoke(xValue, new object[] { yValue }); if (gretValue == ) 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 == ) continue; return m_reverse ? -retValue : retValue;
} return ;
} int CompareWithNull(object left, object right)
{
if ((left == null) && (right == null))
return ; if (left == null)
return -; return ;
} bool HasNull(object left, object right)
{
if (left == null || right == null)
return true;
return false;
}
}
}

需要注意的是DefaultSortComparer<T>是泛型的,并实现了IComparer, IComparer<T>接口,实现了这两个接口,才方便排序。

代码写贴了这么多,用起来怎么用呢。

 var data1 = new SortData() { ID = , Name = "info", Value = "key"};
var data2 = new SortData() { ID = , Name = "64File", Value = "license" };
var data3 = new SortData() { ID = , Name = "cloneToken", Value = "comparer" };
var data4 = new SortData() { ID = , Name = "0est", Value = "backend" }; List<SortData> sortData = new List<SortData>();
sortData.Add(data1);
sortData.Add(data2);
sortData.Add(data3);
sortData.Add(data4); sortData.Sort(new DefaultSortComparer<SortData>(false)); sortData.ForEach(data => Console.WriteLine(data));

结果就不献丑了,经测试,能正常工作。

通过这个例子,就可以看到,要实现它的关键是,Attribute负责装饰类,Reflection负责读取特定Attribute装饰后的元数据信息,实现和特定类类型无关的组件。一次Coding,多次复用。

希望大家多支持,以后会多放一些有意思的开箱即用的代码上来。

Attribute+Reflection,提高代码重用的更多相关文章

  1. MVC引入SERVICE层 提高代码重用性 沟通CONTROL和MODEL

    MVC是web开发中常见的程序结构. 简单的mvc结构如下: view层:显示层. control层:业务层,集合了各种action. model层:模型层,一般和数据打交道.简单的sample:一个 ...

  2. Android 命名规范 (提高代码可以读性)

    android文件众多,根据名称来辨别用途很重要,因此命名要规范 这篇文章可参考:Android 命名规范 (提高代码可以读性) 刚接触android的时候,命名都是按照拼音来,所以有的时候想看懂命名 ...

  3. 精简CSS代码,提高代码的可读性和加载速度

    前言 提高网站整体加载速度的一个重要手段就是提高代码文件的网络传输速度.之前提到过,所有的代码文件都应该是经过压缩了的,这可提高网络传输速度,提高性能.除了压缩代码之外,精简代码也是一种减小代码文件大 ...

  4. “前.NET Core时代”如何实现跨平台代码重用 ——程序集重用

    除了在源代码层面实现共享("前.NET Core时代"如何实现跨平台代码重用 --源文件重用)之外,我们还可以跨平台共享同一个程序集,这种独立于具体平台的"中性" ...

  5. PHP代码重用与函数编写

    代码重用与函数编写 1.使用require()和include()函数 这两个函数的作用是将一个文件爱你载入到PHP脚本中,这样就可以直接调用这个文件中的方法.require()和include()几 ...

  6. 提高代码质量 CheckStyle FindBugs PMD

    提高代码质量-工具篇 注:这是一篇翻译文章,原文:How to improve quality and syntax of your Android code,为了理解连贯,翻译过程中我修改了一些陈述 ...

  7. 谈谈Delphi中的类和对象4---类是一种对数据和操作高度的封装机制 && 类是一种代码重用机制

    五.类是一种对数据和操作高度的封装机制 1)数据封装 unit Unit2; interface type TEmployee = class; private FName: String; publ ...

  8. 从零开始攻略PHP(6)——代码重用与函数编写的一些注意事项

    一个新的项目是这样创建的:它将已有的可重新利用的组件进行组合,并将新的开发难度降低到最小. 代码重用的好处:降低成本.提升可靠性和一致性. 1.使用require()和include()函数 使用一条 ...

  9. 利用Apply的参数数组化来提高代码的优雅性,及高效性

    利用Apply的参数数组化来提高代码的优雅性,及高效性 Function.apply()在提升程序性能方面的技巧 我们先从Math.max()函数说起,Math.max后面可以接任意个参数,最后返回所 ...

随机推荐

  1. try -catch-finally一些要点

    try -catch-finally是处理程序异常时使用,当程序正常时,先走try然后到finally语句,不正常时:程序先走try,然后到catch里面的语句,最后到finally;从上面可以看出, ...

  2. 【Kafka】Broker之Server.properties的重要参数说明

    名称 描述 类型 默认值 有效值区间 重要程度 zookeeper.connect zk地址 string 高 advertised.host.name 过时的:只有当advertised.liste ...

  3. CentOS/Linux 解决 SSH 连接慢

    现在连接linux服务器一般都是使用SSH远程连接的方式.最近新装了一台服务器,发现telnet时速度很快,ping时一切也正常,但SSH连接的时候却很慢.经过网上资料查询,大致是有以下几种原因: 1 ...

  4. JVM的类加载时机

    类加载过程中每个步骤的顺序 我们已经知道,类加载的过程包括:加载.连接.初始化,连接又分为:验证.准备.解析,所以说类加载一共分为5步:加载.验证.准备.解析.初始化. 其中加载.验证.准备.初始化的 ...

  5. Scrum 冲刺博客第三篇

    一.当天站立式会议照片一张 二.每个人的工作 (有work item 的ID),并将其记录在码云项目管理中 昨天已完成的工作 基本的页面设计 各个页面之间的跳转 背景音乐模块设计 今天计划完成的工作 ...

  6. LOJ #6022. 重组病毒

    Description 支持以下操作 1.access一个点 2.问一个点上面的重链的个数 3.换根 , 并access原来的根 Solution 对于重链个数 , 我们在 \(access\) 的时 ...

  7. 解决 swap file “*.swp”already exists!问题

     用vim编辑文件实际上是先copy一份临时文件,病映射到内存给你编辑,编辑的是临时文件,当执行:w后才保存临时文件到原文件,执行:q后才删除临时文件. 每次启动检索是否有临时文件,有就询问如何处理, ...

  8. Nginx proxy buffer相关的设置和解释

    proxy_buffer_size 4k; proxy_buffering on;proxy_buffers 4 4k;proxy_busy_buffers_size 8k;proxy_max_tem ...

  9. Linux进程管理之“四大名捕”

    一.四大名捕 四大名捕,最初出现于温瑞安创作的武侠小说,是朝廷中正义力量诸葛小花的四大徒弟,四人各怀绝技,分别是轻功暗器高手“无情”.内功卓越的高手“铁手”.腿功惊人的“追命”和剑法一流的“冷血”本文 ...

  10. 关于UI回调Invoker的实现(二)

    上篇我说到,光有一个IOperation*的指针,是无法记录这么多事件的.由于无法确定要把回调绑定到哪个事件上,因此,我们需要引入一个中间的传递机制. 没有看到前面的请先查阅上一篇 关于UI回调Inv ...