C# 编程指南
使用属性(C# 编程指南)
属性结合了字段和方法的多个方面。对于对象的用户,属性显示为字段,访问该属性需要完全相同的语法。对于类的实现者,属性是一个或两个代码块,表示一个
get访问器和/或一个
set访问器。当读取属性时,执行 get 访问器的代码块;当向属性分配一个新值时,执行 set 访问器的代码块。不具有 set 访问器的属性被视为只读属性。不具有 get 访问器的属性被视为只写属性。同时具有这两个访问器的属性是读写属性。
与字段不同,属性不作为变量来分类。因此,不能将属性作为
ref(C# 参考)参数或
out(C# 参考)参数传递。
属性具有多种用法:它们可在允许更改前验证数据;它们可透明地公开某个类上的数据,该类的数据实际上是从其他源(例如数据库)检索到的;当数据被更改时,它们可采取行动,例如引发事件或更改其他字段的值。
属性在类模块内是通过以下方式声明的:指定字段的访问级别,后面是属性的类型,接下来是属性的名称,然后是声明
get 访问器和/或
set 访问器的代码模块。例如:
C#
复制代码
public
class
Date
{
private
int
month = 7;
//
"backing store"
public
int
Month
{
get
{
return
month;
}
set
{
if
((value > 0) && (value < 13))
{
month = value;
}
}
}
}
在本例中,
Month 是作为属性声明的,这样
set 访问器可确保
Month 值设置为 1 和 12 之间。
Month 属性使用私有字段来跟踪实际值。属性的数据的真实位置经常称为属性的“后备存储”。属性使用作为后备存储的私有字段是很常见的。将字段标记为私有可确保该字段只能通过调用属性来更改。有关公共和私有访问限制的更多信息,请参见
访问修饰符(C# 编程指南)。
get 访问器
get 访问器体与方法体相似。它必须返回属性类型的值。执行
get 访问器相当于读取字段的值。例如,当正在从
get 访问器返回私有变量并且启用了优化时,对
get 访问器方法的调用由编译器进行内联,因此不存在方法调用的系统开销。然而,由于在编译时编译器不知道在运行时实际调用哪个方法,无法内联虚拟
get 访问器。以下是返回私有字段
name 的值的
get 访问器:
C#
复制代码
class
Person
{
private
string
name;
// the name field
public
string
Name
// the Name property
{
get
{
return
name;
}
}
}
当引用属性时,除非该属性为赋值目标,否则将调用
get 访问器以读取该属性的值。例如:
C#
复制代码
Person p1 =
new
Person();
//...
System.Console.Write(p1.Name);
// the get accessor is invoked here
通过使用
get 访问器更改对象的状态不是一种好的编程风格。例如,以下访问器在每次访问
number 字段时都产生更改对象状态的副作用。
C#
复制代码
private
int
number;
public
int
Number
{
get
{
return
number++;
// Don't do this
}
}
get 访问器可用于返回字段值,或用于计算并返回字段值。例如:
C#
复制代码
class
Employee
{
private
string
name;
public
string
Name
{
get
{
return
name !=
null
? name :
"NA"
;
}
}
}
在上述代码段中,如果不对
Name 属性赋值,它将返回值 NA。
set 访问器
C#
复制代码
class
Person
{
private
string
name;
// the name field
public
string
Name
// the Name property
{
get
{
return
name;
}
set
{
name = value;
}
}
}
当对属性赋值时,用提供新值的参数调用
set 访问器。例如:
C#
复制代码
Person p1 =
new
Person();
p1.Name =
"Joe"
;
// the set accessor is invoked here
System.Console.Write(p1.Name);
// the get accessor is invoked here
在
set 访问器中,对局部变量声明使用隐式参数名称
value 是错误的。
备注
可将属性标记为
public、
private、
protected、
internal 或
protected internal。这些访问修饰符定义类的用户如何才能访问属性。同一属性的
get 和
set 访问器可能具有不同的访问修饰符。例如,
get 可能是
public 以允许来自类型外的只读访问;
set 可能是
private 或
protected。有关更多信息,请参见
访问修饰符(C# 编程指南)。
可以使用
static 关键字将属性声明为静态属性。这使得调用方随时可使用该属性,即使不存在类的实例。有关更多信息,请参见
静态类和静态类成员(C# 编程指南)。
重写虚属性的属性还可以是
sealed的,这表示它对派生类不再是虚拟的。最后一点,属性可以声明为
abstract,这意味着在类中没有实现,派生类必须编写自己的实现。有关这些选项的更多信息,请参见
抽象类、密封类及类成员(C# 编程指南)。
注意
|
示例 1
此例说明了实例、静态和只读属性。它从键盘接受雇员的姓名,按 1 递增
NumberOfEmployees,并显示雇员的姓名和编号。
C#
复制代码
public
class
Employee
{
public
static
int
NumberOfEmployees;
private
static
int
counter;
private
string
name;
// A read-write instance property:
public
string
Name
{
get
{
return
name; }
set
{ name = value; }
}
// A read-only static property:
public
static
int
Counter
{
get
{
return
counter; }
}
// A Constructor:
public
Employee()
{
// Calculate the employee's number:
counter = ++counter + NumberOfEmployees;
}
}
class
TestEmployee
{
static
void
Main()
{
Employee.NumberOfEmployees = 100;
Employee e1 =
new
Employee();
e1.Name =
"Claude Vige"
;
System.Console.WriteLine(
"Employee number: {0}"
, Employee.Counter);
System.Console.WriteLine(
"Employee name: {0}"
, e1.Name);
}
}
输出 1
Employee number: 101
Employee name: Claude Vige
示例 2
此例说明如何访问基类中被派生类中具有同一名称的另一个属性隐藏的属性。
C#
复制代码
public
class
Employee
{
private
string
name;
public
string
Name
{
get
{
return
name; }
set
{ name = value; }
}
}
public
class
Manager : Employee
{
private
string
name;
// Notice the use of the new modifier:
public
new
string
Name
{
get
{
return
name; }
set
{ name = value +
", Manager"
; }
}
}
class
TestHiding
{
static
void
Main()
{
Manager m1 =
new
Manager();
// Derived class property.
m1.Name =
"John"
;
// Base class property.
((Employee)m1).Name =
"Mary"
;
System.Console.WriteLine(
"Name
in
the derived
class
is: {0}"
, m1.Name);
System.Console.WriteLine(
"Name
in
the
base
class
is: {0}"
, ((Employee)m1).Name);
}
}
输出 2
Name in the derived class is: John, Manager
Name in the base class is: Mary
代码讨论
以下是上述示例中的要点:
派生类中的属性
Name 隐藏基类中的属性
Name。在这种情况下,派生类的属性声明中使用
new 修饰符:
C#
复制代码
public
new
string
Name
转换
(Employee) 用于访问基类中的隐藏属性:
C#
复制代码
((Employee)m1).Name =
"Mary"
;
有关隐藏成员的更多信息,请参见
new 修饰符(C# 参考)。
示例 3
在此例中,
Cube 和
Square 这两个类实现抽象类
Shape,并重写它的抽象
Area 属性。注意属性上
override修饰符的使用。程序接受输入的边长并计算正方形和立方体的面积。它还接受输入的面积并计算正方形和立方体的相应边长。
C#
复制代码
abstract
class
Shape
{
public
abstract double Area
{
get
;
set
;
}
}
class
Square : Shape
{
public
double side;
public
Square(double s)
//constructor
{
side = s;
}
public
override double Area
{
get
{
return
side * side;
}
set
{
side = System.Math.Sqrt(value);
}
}
}
class
Cube : Shape
{
public
double side;
public
Cube(double s)
{
side = s;
}
public
override double Area
{
get
{
return
6 * side * side;
}
set
{
side = System.Math.Sqrt(value / 6);
}
}
}
class
TestShapes
{
static
void
Main()
{
// Input the side:
System.Console.Write(
"Enter the side: "
);
double side = double.Parse(System.Console.ReadLine());
// Compute the areas:
Square s =
new
Square(side);
Cube c =
new
Cube(side);
// Display the results:
System.Console.WriteLine(
"Area of the square = {0:F2}"
, s.Area);
System.Console.WriteLine(
"Area of the cube = {0:F2}"
, c.Area);
System.Console.WriteLine();
// Input the area:
System.Console.Write(
"Enter the area: "
);
double area = double.Parse(System.Console.ReadLine());
// Compute the sides:
s.Area = area;
c.Area = area;
// Display the results:
System.Console.WriteLine(
"Side of the square = {0:F2}"
, s.side);
System.Console.WriteLine(
"Side of the cube = {0:F2}"
, c.side);
}
}
输入
4
24
输出 3
Enter the side: 4
Area of the square = 16.00
Area of the cube = 96.00
Enter the area: 24
Side of the square = 4.90
Side of the cube = 2.00
(来源:msdn )