Effective C# Item 24: Prefer Declarative to Imperative Programming

Effective C# Item 24: Prefer Declarative to Imperative Programming

      声明式编程是一种简洁的程序行为描述方式。声明式编程让我们可以通过使用声明来达到定义程序行为的目的。在C#或者其它的编程语言中,命令式编程是最为常见的:我们通过编写方法来定义程序的行为。我们可以通过C#中的属性来使用声明式编程。我们可以为类,方法,属性和数据成员声明属性,在运行时.Net会自动为我们添加这些声明的行为。这种声明方法比命令式编程更简单,也更容易理解。

      让我们从一个简单的例子开始。当我们编写第一个ASP.Net的Web Service时,设计器会自动生成下面一段代码:

None.gif [WebMethod]
None.gif
public   string  HelloWorld()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
return "Hello World";
ExpandedBlockEnd.gif}

      在HelloWorld()方法上有一个[WebMethod]方法。它声明了HelloWorld是一个Web Service方法。在运行时,ASP.Net会对此属性作出响应,创建WSDL(Web服务描述语言)文档。另外我们还可以通过ASP.Net动态创建的HTML页在IE中测试我们的Web服务。上述这些动作都表现在WebMethod属性中。我们通过声明这种属性,就可以在运行时获得所期望的支持。使用这种属性可以提高我们的编程效率并减少可能发生的错误。

      在ASP.Net运行时使用反射来检测类中的WebMethod。ASP.Net可以在运行时自动添加所需的代码将这些方法转变为WebMethod。

      [WebMethod]属性只是.Net类库中定义的大量属性中的一个。这些属性可以帮助我们更快的完成编程工作。例如有些属性可以帮助我们创建序列化类型[Serializable],还有可以控制条件编译[Conditional]。我们应当更多的使用这些属性,而不是自己编写代码来达到目的。

      如果预定义的属性不能满足我们的要求,我们也可以通过自定义属性和反射机制来创建我们自己的声明式编程结构。举例来说,我们希望创建一个可以设置默认排序的属性,通过使用这个属性,我们可以将该类中的某个属性设定为默认的排序比较项。下例中是我们希望达到的自定义属性的效果:

None.gif [DefaultSort( " Name " )]
None.gif
public   class  Customer
ExpandedBlockStart.gifContractedBlock.gif
dot.gif {
InBlock.gif    
private string _name;
InBlock.gif    
public string Name
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
get
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
return _name;
ExpandedSubBlockEnd.gif        }

InBlock.gif        
set
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            _name 
= value;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

InBlock.gif
InBlock.gif    
private string _balance;
InBlock.gif    
public string CurrentBalance
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        
get
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
return _balance;
ExpandedSubBlockEnd.gif        }

ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}

      在上例的声明中,Customers类的对象在任何集合中的默认排序应当根据Name的大小顺序来确定。DefaultSort属性并不是.Net Framework中已有的。要实现这种效果,我们首先要创建一个DefaultSortAttribute类:

None.gif     [AttributeUsage(AttributeTargets.Class  |  AttributeTargets.Struct)]
None.gif    
public   class  DefaultSortAttribute : Attribute
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private string _name;
InBlock.gif        
public string Name
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return _name;
ExpandedSubBlockEnd.gif            }

InBlock.gif            
set
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                _name 
= value;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public DefaultSortAttribute(string name)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            _name 
= name;
ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      我们仍然需要为DefaultSort属性写一段为集合中对象排序的代码。我们可以使用反射来找到需要的属性并且对它们进行比较。这样做的好处在于我们只需要写一遍代码。

      接下来我们需要创建一个实现IComparer接口的类。IComparer接口有一个CompareTo()方法可以用来比较两个给定的对象。通过它我们可以定义排序的规则。这个类通过构造函数来确定使用哪一个默认属性来进行比较排序。Compare方法对相隔对象的默认排序属性进行比较。

None.gif      internal   class  GenericComparer : IComparer
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private readonly PropertyDescriptor _sortProp;
InBlock.gif        
private readonly bool _reverse = false;
InBlock.gif
InBlock.gif        
public GenericComparer(Type t)
InBlock.gif            : 
this(t, false)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        
public GenericComparer(Type t, bool reverse)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            _reverse 
= reverse;
InBlock.gif            
object[] a = t.GetCustomAttributes(typeof(DefaultSortAttribute), reverse);
InBlock.gif            
if (a.Length > 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                DefaultSortAttribute sortName 
= a[0as DefaultSortAttribute;
InBlock.gif                
string name = sortName.Name;
InBlock.gif                PropertyDescriptorCollection props 
= TypeDescriptor.GetProperties(t);
InBlock.gif                
if (props.Count > 0)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
foreach (PropertyDescriptor p in props)
ExpandedSubBlockStart.gifContractedSubBlock.gif                    
dot.gif{
InBlock.gif                        
if (p.Name == name)
ExpandedSubBlockStart.gifContractedSubBlock.gif                        
dot.gif{
InBlock.gif                            _sortProp 
= p;
InBlock.gif                            
break;
ExpandedSubBlockEnd.gif                        }

ExpandedSubBlockEnd.gif                    }

ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
ContractedSubBlock.gifExpandedSubBlockStart.gif        
IComparer 成员#region IComparer 成员
InBlock.gif
InBlock.gif        
int IComparer.Compare(object left, object right)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
if ((left == null&& (right == null))
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return 0;
ExpandedSubBlockEnd.gif            }

InBlock.gif            
if (left == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return -1;
ExpandedSubBlockEnd.gif            }

InBlock.gif            
if (right == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return 1;
ExpandedSubBlockEnd.gif            }

InBlock.gif            
if (_sortProp == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return 0;
ExpandedSubBlockEnd.gif            }

InBlock.gif            IComparable lField 
= _sortProp.GetValue(left) as IComparable;
InBlock.gif            IComparable rField 
= _sortProp.GetValue(right) as IComparable;
InBlock.gif            
int rVal = 0;
InBlock.gif            
if (lField == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
if (rField == null)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
return 0;
ExpandedSubBlockEnd.gif                }

InBlock.gif                
else
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
return -1;
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

InBlock.gif            rVal 
= lField.CompareTo(rField);
InBlock.gif            
return (_reverse) ? -rVal : rVal;
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockEnd.gif        
#endregion

ExpandedBlockEnd.gif    }

      对于任何声明有DefaultSort属性的类,GenericComparer都可以使用其默认排序属性对其进行排序。

None.gif CustomerList.Sort( new  GenericComparer( typeof (Customer)));

      在实现GenericComparer时我们使用了诸如反射之类的技巧。这样的好处在于我们只需要写一遍代码就可以适用于各种情况。我们只需要为类添加属性声明,就可以在集合中对这些类的对象进行排序。如果我们改变DefaultSort属性,那些声明该属性的类的行为也会跟着改变。我们不必为每个类都改变其算法的代码。

      使用声明的方式可以让我们减少重复性的代码。再举GenericComparer的例子,它的优势在于只需要创建一个类就可以通过声明的方式来为每个类型创建行为。关键在于这些类的行为的创建是基于不同的声明,而不是算法的改变。对于任何声明为DefaultSort属性的类型,GenericComparer都会起作用。如果这种排序工作在我们的应用程序中只是出现一两次的话,我们完全可以使用传统的方式来实现。但是如果我们需要用到数十次这种行为时,声明的方式会让我们节省更多的时间,消耗更少的精力。除了声明之外,我们不需要为其写任何代码。

      声明式编程是我们的一件有力的武器。它可以让我们尽量避免在编写大量类似算法时产生逻辑错误,创建更简洁易读的代码。

      译自   Effective C#:50 Specific Ways to Improve Your C#                      Bill Wagner著

      回到目录
 

转载于:https://www.cnblogs.com/aiyagaze/archive/2007/03/05/663077.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值