Effective C# Item 1: Always Use Properties Instead of Accessible Data Members

Effective C# Item1:Always Use Properties Instead of Accessible Data Members

      在C#语言中,属性的地位已经从编程习惯提升至语言的特性(这里用到first-class language。这个含义可以参见wikipedia)。你不必为你的类型创建公有数据成员,也不必手工创建属性中的get和set方法。属性让你可以通过公有接口暴露数据成员,同时又可以提供oo环境中你所希望的封装。属性在可达性上就像一个公有数据成员,但是实现上像一个方法。

      对于一些类型的成员来讲,例如客户的姓名、xy坐标等,数据型是它们最好的表现形式。属性允许你创建一个接口,使得对它们的操作可以像公有数据成员一样,同时又保留了使用成员函数一样的优势。

      在.net framework中已经假设你会为你的公有数据成员使用属性。实际上,在.net framework的数据绑定中支持的也是属性而不是公有的数据成员。不论是web控件还是win form控件,都是通过属性来实现数据绑定的。下面的例子中,数据绑定机制通过反射在已知类型中寻找特定属性:

None.gif textBoxCity.DataBindings.Add( " Text " , address,  " City " );

      上面的代码将adress中的City属性绑定到名为textBoxCity控件的Text属性上。如果City不是属性而是adress中的一个公有成员,上面的代码就不能实现。

      当遇到新的需求或行为变更时,属性更容易修改。例如你现在决定客户的名字不能为空,那你只要在对应属性的set中添加判断就可以了。

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                
if ((value == null|| (value.Length == 0))
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
throw new ArgumentException();
ExpandedSubBlockEnd.gif                }

InBlock.gif                _name 
= value;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      但是如果你使用的是公有数据成员,那你必须检查你所有的代码并一一添加判断,那将消耗大量的时间。

      属性对于多线程的操作支持也很方便。一般的只要在get和set时加锁就可以了。

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                
lock (this)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    
return _name;
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

InBlock.gif            
set
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
lock (this)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
InBlock.gif                    _name 
= value;
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      属性拥有方法的语言特性。属性可以为虚拟成员(virtual)

None.gif      public   class  Customer
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private string _name;
InBlock.gif        
public virtual 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
InBlock.gif                _name 
= value;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      同样属性可以是抽象的(abstract)或是接口定义中的一部分

None.gif      public   interface  ICustomer
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
string Name
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get;
ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      我们还可以通过接口属性来创建常量

None.gif      public   interface  ICustomer
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
string Name
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get;
InBlock.gif            
//没有set
ExpandedSubBlockEnd.gif
        }

ExpandedBlockEnd.gif    }

None.gif    
public   class  Customer : ICustomer
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
string _name;
InBlock.gif
ContractedSubBlock.gifExpandedSubBlockStart.gif        
ICustomer Members#region ICustomer Members
InBlock.gif
InBlock.gif        
string ICustomer.Name
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif            
get dot.gifthrow new Exception("The method or operation is not implemented."); }
ExpandedSubBlockEnd.gif        }

InBlock.gif
ExpandedSubBlockEnd.gif        
#endregion

ExpandedBlockEnd.gif    }

      在C#2.0中可以对get和set设定不同的可达性,这使得程序可以更加可控。下例中对Name属性的set为protected

None.gif      public   class  Customer
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private string _name;
InBlock.gif        
public virtual string Name
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return _name;
ExpandedSubBlockEnd.gif            }

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

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      属性不仅可以应用在简单数据类型领域。你可以使用索引来创建返回序列项的属性:

None.gif      public   class  Customer
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private string[] _name;
InBlock.gif        
public string this[int index]
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return _name[index];
ExpandedSubBlockEnd.gif            }

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

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      索引拥有所有简单属性所包含的特性,可以在索引内部进行校验和计算,可以为虚拟或抽象成员,可以在接口中声明,可以设为只读或读写。一维整型参数的索引可以参与数据绑定,其他非整型参数索引可以定义映射(map)和字典(dictionary)。

      C#中你可以创建多维索引如下例所示:

None.gif      public   class  Customer
ExpandedBlockStart.gifContractedBlock.gif    
dot.gif {
InBlock.gif        
private string[,] _name;
InBlock.gif        
public string this[int index1,int index2]
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return _name[index1,index2];
ExpandedSubBlockEnd.gif            }

InBlock.gif            
set
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                _name[index1,index2] 
= value;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif        
//当然这里string型的index2是不行的了,我只是举个例子而已
InBlock.gif
        public string this[int index1, string index2]
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            
get
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
return _name[index1, index2];
ExpandedSubBlockEnd.gif            }

InBlock.gif            
set
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                _name[index1, index2] 
= value;
ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

ExpandedBlockEnd.gif    }

      注意所有的索引在声明时都使用this关键字。索引不能自定义命名。在一个类型中,同样参数的索引最多只能有一个。

      虽然属性有很多好处,但是有人会依旧先创建公有成员,当需要用到属性的特性时在将其修改为属性。这看起来好像是蛮有道理的,不过这是不好的做法。对于属性和公有成员来说,它们的实现代码是一致的,这会为我们造成误解,认为只要修改公有成员为属性就万事大吉了。实际上这种说法只是部分的正确。

      在C#的源码中使用属性和公有成员的代码虽然相同,但通过编译器生成的中间语言却是不同的。它们是代码兼容的,重新编译就可以正确运行,但却不是二进制兼容的。(对于二进制兼容这点,我也不是十分清楚… 我觉得这种兼容是应该与开发平台无关的,因此上面的改变才会引发不兼容性。望指教)。

      在效率上虽然使用属性并不会比使用公有数据成员快,但是一般情况下也不会比它慢。属性在JIT编译器编译时做为内联函数(inline)处理,相当程度上减少了效率的损失。

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

     P.S.  Item1还是比较简单的,就不写什么了。在我刚参加工作的时候就在老一辈的指导下养成了这个习惯,虽然声明时候会麻烦一些,但是比起事后的修改工作实在是好太多了… 另外可将其在不同权限下分别设为只读或读写非常实用。

      回到目录

转载于:https://www.cnblogs.com/aiyagaze/archive/2006/08/29/489862.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值