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为自己定义的类定义的默认排序行为。

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
    
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[0as 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参数的改变来改变程序的行为,很大程度上提高了写代码的效率,减少了代码冗余以及出错的机会,并且更易读,更容易理解。基于声明式编程,只用修改一个声明,就能改变程序的逻辑,而不用像以前一样重写代码,那么还等什呢,用声明式编程吧。 

[Serializable]
public class Student
{
    
private string _name;
    
private int _age;
}

 

posted @ 2009-09-14 14:07  PeterLau  阅读(553)  评论(1)    收藏  举报