Item 24:选择声明式编程而不是命令式编程
声明式编程通常比命令式编程更能简单,清楚的描述软件的行为。声明式编程意味着你能通过一个简单的声明来定义你的程序的行为,而不是传统的写代码来定义程序的行为(比如在类中通过定义方法来定义软件的行为)。那么在C#中怎么实现声明式编程呢,其实就是通过C#的Attribute来实现的,你可以将Attribute添加到类,方法,属性,字段的定义上面,然后.NET运行时为你产生具体的行为(即该声明所对应的代码)。由此看来,声明式编程更加容易实现,可读性更强,并且减少了出错机会(毕竟那部分代码是由.NET运行时为你产生的)。
下面让我们看一个典型的使用Attribute的例子:
上面的例子对Student类使用了[Serializable]属性,其作用是声明这个Student类可以被序列化,实际上在.NET运行时为[Serializable]这个Attribute创建了一个Iserializable.GetObjectData(SerializationInfo,StreamingContext)方法用于在序列化时将对象的成员序列化到流中,还创建了一个只能被.net运行时调用的构造函数,用于将已序列化的对象从流中反序列化为对象。
如果.net预定义的Attribute不能满足你的需要的话,你可以自定义Attribute通过反射来创建你自己的声明式编程的结构。下面还是来看一个例子:该例子定义了一个自定义Attribute,并添加了相关的代码,让客户端通过使用这个Attribute为自己定义的类定义的默认排序行为。
public class DefaultSortAttribute : System.Attribute
{
private string _name;
public string Name
{
get { return _name; }
set { _name = value; }
}
public DefaultSortAttribute(string name)
{
_name = name;
}
}
[DefaultSort("Name")]
public class Customer
{
public Customer(string name)
{
Name = name;
}
public string Name
{
get;
set;
}
public decimal CurrentBalance
{
get;
set;
}
public decimal AccountValue
{
get;
set;
}
}
internal class GenericComparer : IComparer
{
private readonly PropertyDescriptor _sortProp;
public GenericComparer(Type t)
{
object[] attArray=t.GetCustomAttributes(typeof(DefaultSortAttribute),false);
if(attArray.Length>0)
{
DefaultSortAttribute sortName=attArray[0] as DefaultSortAttribute;
string name=sortName.Name;
PropertyDescriptorCollection props=TypeDescriptor.GetProperties(t);
if(props.Count>0)
{
foreach(PropertyDescriptor p in props)
{
if(p.Name==name)
{
_sortProp=p;
break;
}
}
}
}
}
int IComparer.Compare(object left,object right)
{
if((left==null)&&(right==null))
return 0;
if(left==null)
return -1;
if(right==null)
return 1;
if(_sortProp==null)
{
return 0;
}
IComparable lField = _sortProp.GetValue(left) as IComparable;
IComparable rField = _sortProp.GetValue(right) as IComparable;
int rValue = 0;
if (lField == null)
{
if (right == null)
return 0;
else
return -1;
}
rValue = lField.CompareTo(rField);
return rValue;
}
}
class Program
{
static void Main(string[] args)
{
ArrayList customerList = new ArrayList();
customerList.Add(new Customer("liu"));
customerList.Add(new Customer("jun"));
customerList.Add(new Customer("pei"));
customerList.Sort(new GenericComparer(typeof(Customer)));
foreach (Customer c in customerList)
{
Console.Write(c.Name + " "); //输出“jun liu pei”
}
}
}
如上例所示,开始我自定义了一个Attribute,名为DefaultSortAttribute,其上的[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]其实也是
一个Attribute,用于声明DefaultSortAttribute只能用于Class和Struct。然后定义了一个Customer类,并在前面添加了[DefaultSort("Name")],传入的参数Name表示
该Customer类型的默认排序规则是基于其Name属性值的。接着我定义了一个GenericComparer,该类实现了IComparer接口,其中的Comparer方法用于提供多个对象在集合中的排序规则。首先在GenericComparer的构造函数中通过反射技术(GetCustomAttributes方法和GetProperties方法)获取应用于Customer类的Attribute,并遍历Customer类的Property,通过获取的Attribute和Property的名称进行比较找出用于排序的属性。最后,在Compare方法中基于找到的Property值对Customer对象进行排序。
上面说了一大堆,其实也挺简单,就是利用Attribute,通过反射技术,找到要用于排序的Property。
很明显,如果这时要基于Customer类的AccountValue进行排序,那么客户端只用更改Customer之上DefaultSort Attribute的参数就可以完成排序行为的转换,而不用重新定义排序的逻辑并编写代码。所以,声明式编程能让我们只写一次代码,而后通过Attribute参数的改变来改变程序的行为,很大程度上提高了写代码的效率,减少了代码冗余以及出错的机会,并且更易读,更容易理解。基于声明式编程,只用修改一个声明,就能改变程序的逻辑,而不用像以前一样重写代码,那么还等什呢,用声明式编程吧。
public class Student
{
private string _name;
private int _age;
}

浙公网安备 33010602011771号