详解C#中的接口属性以及属性访问器的访问限制
接口属性
可以在接口上声明属性。以下是接口索引器访问器的示例:
publicinterfaceISampleInterface { //Propertydeclaration: stringName { get; set; } }
接口属性的访问器不具有体。因此,访问器的用途是指示属性是否为读写、只读或只写。
在此例中,接口IEmployee具有读写属性Name和只读属性Counter。Employee类实现IEmployee接口并使用这两种属性。程序读取新雇员的姓名和雇员的当前编号,并显示雇员姓名和计算所得的雇员编号。
可以使用属性的完全限定名,它引用声明成员的接口。例如:
stringIEmployee.Name { get{return"EmployeeName";} set{} }
这称为显式接口实现(C#编程指南)。例如,如果Employee类实现两个接口ICitizen和IEmployee,并且两个接口都具有Name属性,则需要显式接口成员实现。即,如下属性声明:
stringIEmployee.Name { get{return"EmployeeName";} set{} }
在IEmployee接口上实现Name属性,而下面的声明:
stringICitizen.Name { get{return"CitizenName";} set{} }
在ICitizen接口上实现Name属性。
interfaceIEmployee { stringName { get; set; } intCounter { get; } } publicclassEmployee:IEmployee { publicstaticintnumberOfEmployees; privatestringname; publicstringName//read-writeinstanceproperty { get { returnname; } set { name=value; } } privateintcounter; publicintCounter//read-onlyinstanceproperty { get { returncounter; } } publicEmployee()//constructor { counter=++counter+numberOfEmployees; } } classTestEmployee { staticvoidMain() { System.Console.Write("Enternumberofemployees:"); Employee.numberOfEmployees=int.Parse(System.Console.ReadLine()); Employeee1=newEmployee(); System.Console.Write("Enterthenameofthenewemployee:"); e1.Name=System.Console.ReadLine(); System.Console.WriteLine("Theemployeeinformation:"); System.Console.WriteLine("Employeenumber:{0}",e1.Counter); System.Console.WriteLine("Employeename:{0}",e1.Name); } }
比如这里我们输入:
210 HazemAbolrous
则示例输出
Enternumberofemployees:210 Enterthenameofthenewemployee:HazemAbolrous Theemployeeinformation: Employeenumber:211 Employeename:HazemAbolrous
限制访问器可访问性
属性或索引器的get和set部分称为“访问器”。默认情况下,这些访问器具有相同的可见性或访问级别:其所属属性或索引器的可见性或访问级别。不过,有时限制对其中某个访问器的访问会很有用。通常是在保持get访问器可公开访问的情况下,限制set访问器的可访问性。例如:
privatestringname="Hello"; publicstringName { get { returnname; } protectedset { name=value; } }
在此示例中,名为Name的属性定义了一个get访问器和一个set访问器。get访问器接受该属性本身的可访问性级别(在此示例中为public),而对于set访问器,则通过对该访问器本身应用protected访问修饰符来进行显式限制。
对访问器的访问修饰符的限制
对属性或索引器使用访问修饰符受以下条件的制约:
不能对接口或显式接口成员实现使用访问器修饰符。
仅当属性或索引器同时具有set和get访问器时,才能使用访问器修饰符。这种情况下,只允许对其中一个访问器使用修饰符。
如果属性或索引器具有override修饰符,则访问器修饰符必须与重写的访问器的访问器(如果有的话)匹配。
访问器的可访问性级别必须比属性或索引器本身的可访问性级别具有更严格的限制。
重写访问器的访问修饰符
在重写属性或索引器时,被重写的访问器对重写代码而言,必须是可访问的。此外,属性/索引器和访问器的可访问性级别都必须与相应的被重写属性/索引器和访问器匹配。例如:
publicclassParent { publicvirtualintTestProperty { //Noticetheaccessoraccessibilitylevel. protectedset{} //Noaccessmodifierisusedhere. get{return0;} } } publicclassKid:Parent { publicoverrideintTestProperty { //Usethesameaccessibilitylevelasintheoverriddenaccessor. protectedset{} //Cannotuseaccessmodifierhere. get{return0;} } }
实现接口
使用访问器实现接口时,访问器不能具有访问修饰符。但是,如果使用一个访问器(如get)实现接口,则另一个访问器可以具有访问修饰符,如下面的示例所示:
publicinterfaceISomeInterface { intTestProperty { //Noaccessmodifierallowedhere //becausethisisaninterface. get; } } publicclassTestClass:ISomeInterface { publicintTestProperty { //Cannotuseaccessmodifierherebecause //thisisaninterfaceimplementation. get{return10;} //Interfacepropertydoesnothavesetaccessor, //soaccessmodifierisallowed. protectedset{} } }
访问器可访问性域
如果对访问器使用访问某个修饰符,则访问器的可访问性域由该修饰符确定。
如果不对访问器使用访问修饰符,则访问器的可访问性域由属性或索引器的可访问性级别确定。
下面的示例包含三个类:BaseClass、DerivedClass和MainClass。每个类的BaseClass、Name和Id都有两个属性。该示例演示在使用限制性访问修饰符(如protected或private)时,如何通过BaseClass的Id属性隐藏DerivedClass的Id属性。因此,向该属性赋值时,将调用BaseClass类中的属性。将访问修饰符替换为public将使该属性可访问。
该示例还演示DerivedClass的Name属性的set访问器上的限制性访问修饰符(如private或protected)如何防止对该访问器的访问,并在向它赋值时生成错误。
publicclassBaseClass { privatestringname="Name-BaseClass"; privatestringid="ID-BaseClass"; publicstringName { get{returnname;} set{} } publicstringId { get{returnid;} set{} } } publicclassDerivedClass:BaseClass { privatestringname="Name-DerivedClass"; privatestringid="ID-DerivedClass"; newpublicstringName { get { returnname; } //Using"protected"wouldmakethesetaccessornotaccessible. set { name=value; } } //UsingprivateonthefollowingpropertyhidesitintheMainClass. //AnyassignmenttothepropertywilluseIdinBaseClass. newprivatestringId { get { returnid; } set { id=value; } } } classMainClass { staticvoidMain() { BaseClassb1=newBaseClass(); DerivedClassd1=newDerivedClass(); b1.Name="Mary"; d1.Name="John"; b1.Id="Mary123"; d1.Id="John123";//TheBaseClass.Idpropertyiscalled. System.Console.WriteLine("Base:{0},{1}",b1.Name,b1.Id); System.Console.WriteLine("Derived:{0},{1}",d1.Name,d1.Id); //Keeptheconsolewindowopenindebugmode. System.Console.WriteLine("Pressanykeytoexit."); System.Console.ReadKey(); } }
输出:
Base:Name-BaseClass,ID-BaseClass Derived:John,ID-BaseClass