接口属性
可以在接口上声明属性。以下是接口索引器访问器的示例:
public interface ISampleInterface
{
// Property declaration:
string Name
{
get;
set;
}
}
接口属性的访问器不具有体。因此,访问器的用途是指示是否为读写、只读或只写。
示例
在此例中,接口IEmployee具有读写属性Name和只读属性Counter。Employee类实现IEmployee接口并使用这两种属性。程序读取新雇员的姓名和雇员的当前编号,并显示雇员姓名和计算机所得的雇员编号。
可以使用属性的完全限定名,它引用声明成员的接口。例如:
string IEmployee.Name
{
get { return "Employee Name"; }
set { }
}
这被称为显式接口实现。例如,如果Employee类实现两个接口ICitizen和IEmployee,并且两个接口都具有Name属性,则需要显式接口成员实现。即,如下属性声明:
string IEmployee.Name
{
get { return "Employee Name"; }
set { }
}
在IEmployee接口上实现Name属性,而下面的声明:
string ICitizen.Name
{
get { return "Citizen Name"; }
set { }
}
在ICitizen接口上实现Name属性。
string Name{
get;
set;
}
int Counter{
get;
}
}
public class Employee : IEmployee {
public static int numberOfEmployees;
private string name;
public string Name{ //read-write instance property
get{
return name;
}
set{
name = value;
}
}
private int counter;
public int Counter{ //read-only instance property
get{
return counter;
}
}
public Employee(){ //constructor
counter = ++counter + numberOfEmployees;
}
}
class TestEmployee2 {
static void Main(){
System.Console.Write("Enter number of employees: ");
Employee.numberOfEmployees = int.Parse(System.Console.ReadLine());
Employee e1 = new Employee();
System.Console.Write("Enter the name of the new employee: ");
e1.Name = System.Console.ReadLine();
System.Console.WriteLine("The employee information:");
System.Console.WriteLine("Employee number: {0}",e1.Counter);
System.Console.WriteLine("Employee name: {0}",e1.Name);
}
}
非对称访问器可访问性
属性或索引器的get和set部分称为“访问器”。默认情况下,这些访问器具有相同的可见性或访问级别:其所属属性或索引器的可见性或访问级别。不过,有时限制对其中某个访问器的访问会很有用。通常是在保持get访问器可公开访问的情况下,限制set访问器的可访问性。例如:
public string Name
{
get
{
return name;
}
protected set
{
name = value;
}
}
在此示例中,名为Name的属性定义了一个get访问器和一个set访问器。Get访问器接受该属性本身的可访问性级别(在此例中为public),而对于set访问器,则通过对该访问器本身应用protected访问修饰符来进行显式限制。
对属性或索引器使用访问修饰符受以下条件的制约:
l 不能对接口或显式接口成员实现使用访问器修饰符
l 仅当属性或索引器同时具有set和get访问器时,才能使用访问器修饰符。这种情况下,只允许对其中一个访问器使用修饰符
l 如果属性或索引器具有override修饰符,则访问器修饰符必须与重写的访问器的访问器(如果有的话)匹配
l 访问器的可访问性级别必须比属性或索引器本身的可访问性级别具有更严格的限制
重写访问器的访问修饰符
在重写属性或索引器时,被重写的访问器对重写代码而言,必须是可访问的。此外,属性/索引器和访问性级别都必须与相应的被重写属性/索引器和访问器匹配。例如:
public class Parent
{
public virtual int TestProperty
{
// Notice the accessor accessibility level.
protected set { }
// No access modifier is used here.
get { return 0; }
}
}
public class Kid : Parent
{
public override int TestProperty
{
// Use the same accessibility level as in the overridden accessor.
protected set { }
// Cannot use access modifier here.
get { return 0; }
}
}
实现接口
使用访问器实现接口时,访问器不能具有访问修饰符。但是,如果使用一个访问器(如get)实现接口,则另一个访问器可以具有访问修饰符,如下面的示例所示:
public interface ISomeInterface
{
int TestProperty
{
// No access modifier allowed here
// because this is an interface.
get;
}
}
public class TestClass : ISomeInterface
{
public int TestProperty
{
// Cannot use access modifier here because
// this is an interface implementation.
get { return 10; }
// Interface property does not have set accessor,
// so access modifier is allowed.
protected set { }
}
}
访问器可访问性域
如果对访问器使用访问某个修饰符,则访问器的可访问性域由该修饰符确定。
如果不对访问器使用访问修饰符,则访问器的可访问性域由属性或索引器的可访问性级别确定。
示例
下面的示例包含三个类:BaseClass、DerivedClass和MainClass。每个类的BaseClass、Name和Id都有两个属性。该示例演示在使用限制性访问修饰符(如protected或private)时,如果通过BaseClass的Id属性隐藏DerivedClass的Id属性。因此,向该属性赋值时,将调用BaseClass类中的属性。将访问修饰符替换为public将使该属性可访问。
该示例还演示了DerivedClass的Name属性的set访问器上的限制性访问修饰符(如private或protected)如何防止对该访问器的访问,并在向它赋值时生成错误。
private string name = "Name-BaseClass";
private string id = "ID-BaseClass";
public string Name{
get{
return name;
}
set{
}
}
public string Id{
get{
return id;
}
set{
}
}
}
public class DerivedClass : BaseClass {
private string name = "Name-DerivedClass";
private string id = "ID-DerivedClass";
new public string Name{
get{
return name;
}
//using "protected" would make the set accessor not accessible
set{
name = value;
}
}
//using private on the following property hides it in the Main Class.
//Any assignment to the property will use Id in BaseClass.
new private string Id{
get{
return id;
}
set{
id = value;
}
}
}
class MainClass {
static void Main(){
BaseClass b1 = new BaseClass();
DerivedClass d1 = new DerivedClass();
b1.Name = "Mary";
d1.Name = "John";
b1.Id = "Mary123";
d1.Id = "John123"; //The BaseClass.Id property is called.
System.Console.WriteLine("Base: {0}, {1}",b1.Name,b1.Id);
System.Console.WriteLine("Derived: {0}, {1}",d1.Name,d1.Id);
}
}
如何:声明和使用读/写属性
属性可以提供公共数据成员的便利,而又不会带来不受保护、不受控制以及未经验证访问对象数据的风险。这是通过“访问器”来实现的:访问器是为基础数据成员赋值和检索其值的特殊方法。使用set访问器可以为数据成员赋值,使用get访问器可以检索数据成员的值。
此示例演示Person类,该类具有两个属性:Name(string)和Age(int)。这两个属性都提供get和set访问器,因此它们被视为读/写属性。
private string m_name = "N/A";
private int m_Age = 0;
//Declare a Name property of type string:
public string Name{
get{
return m_name;
}
set{
m_name = value;
}
}
//Declare an Age property of type int:
public int Age{
get{
return m_Age;
}
set{
m_Age = value;
}
}
public override string ToString(){
return "Name = " + Name + ", Age = " + Age;
}
}
class TestPerson {
static void Main(){
//Create a new Person object:
Person person = new Person();
//Print out the name and the age associated with the person:
System.Console.WriteLine("Person details - {0}",person);
//Set some values on the person object:
person.Name = "Joe";
person.Age = 99;
System.Console.WriteLine("Person details - {0}",person);
//Increment the Age property:
person.Age += 1;
System.Console.WriteLine("Person details - {0}",person);
}
}
可靠编程
在上面的示例中,Name和Age属性是公共的,并且同时包含get和set访问器。这允许任何对象读写这些属性。不过,有时需要排除其中的一个访问器。例如,省略set访问器将使该属性称为只读的:
public string Name
{
get
{
return m_name;
}
}
此外,您还可以公开一个访问器,而使另一个访问器称为私有的或受保护的。
声明了属性后,可像使用类的字段那样使用这些属性。这使得获取和设置属性值时都可以使用非常自然的语法,如以下语句只能够所示:
person.Name = "Joe";
person.Age = 99;
注意,属性set方法中可以使用一个特殊的value变量。该变量包含用户指定的值,例如:
m_name = value;
请注意用于使Person对象上的Age属性递增的简介语法:
person.Age += 1;
如果将单独的set和get方法用于模型属性,则等效代码可能类似于:
person.SetAge(person.GetAge() + 1);
本示例中重写了ToString方法:
public override string ToString()
{
return "Name = " + Name + ", Age = " + Age;
}
注意,程序中未显示使用ToString。默认情况下,它由WriteLine调用来调用。