ECMA-334 C# Language Specification17.6.2: Accessors |
The accessor-declarations
of a property specify the executable statements associated with reading and writing that property.
get-accessor-declaration
set-accessor-declaration
opt set-accessor-declaration
get-accessor-declaration
opt attributes
opt get
accessor-body
attributes
opt set
accessor-body
block
;
The accessor declarations consist of a get-accessor-declaration
, a set-accessor-declaration
, or both. Each accessor declaration consists of the token get or set followed by an accessor-body
. For abstract and extern properties, the accessor-body
for each accessor specified is simply a semicolon. For the accessors of any non-abstract, non-extern property, the accessor-body
is a block which specifies the statements to be executed when the corresponding accessor is invoked.
A get accessor corresponds to a parameterless method with a return value of the property type. Except as the target of an assignment, when a property is referenced in an expression, the get accessor of the property is invoked to compute the value of the property (14.1.1). The body of a get accessor must conform to the rules for value-returning methods described in 17.5.8. In particular, all return statements in the body of a get accessor must specify an expression that is implicitly convertible to the property type. Furthermore, the endpoint of a get accessor must not be reachable.
A set accessor corresponds to a method with a single value parameter of the property type and a void return type. The implicit parameter of a set accessor is always named value. When a property is referenced as the target of an assignment (14.13), or as the operand of ++ or (14.5.9, --14.6.5), the set accessor is invoked with an argument (whose value is that of the right-hand side of the assignment or the operand of the ++ or --operator) that provides the new value (14.13.1). The body of a set accessor must conform to the rules for void methods described in 17.5.8. In particular, return statements in the set accessor body are not permitted to specify an expression. Since a set accessor implicitly has a parameter named value, it is a compile-time error for a local variable declaration in a set accessor to have that name.
Based on the presence or absence of the get and set accessors, a property is classified as follows:
the Button control declares a public Caption property. The get accessor of the Caption property returns the string stored in the private caption field. The set accessor checks if the new value is different from the current value, and if so, it stores the new value and repaints the control. Properties often follow the pattern shown above: The get accessor simply returns a value stored in a private field, and the set accessor modifies that private field and then performs any additional actions required to fully update the state of the object.
public class Button: Control
{
private string caption;
public string Caption {
get {
return caption;
}
set {
if (caption != value) {
caption = value;
Repaint();
}
}
}
public override void Paint(Graphics g, Rectangle r) {
// Painting code goes here
}
}
Button okButton = new Button();
okButton.Caption = "OK"; // Invokes set accessor
string s = okButton.Caption; // Invokes get accessor
The get and set accessors of a property are not distinct members, and it is not possible to declare the accessors of a property separately.
does not declare a single read-write property. Rather, it declares two properties with the same name, one read-only and one write-only. Since two members declared in the same class cannot have the same name, the example causes a compile-time error to occur. end example]
class A
{
private string name;
public string Name { // Error, duplicate member name
get { return name; }
}
public string Name { // Error, duplicate member name
set { name = value; }
}
}
When a derived class declares a property by the same name as an inherited property, the derived property hides the inherited property with respect to both reading and writing.
the P property in B hides the P property in A with respect to both reading and writing. Thus, in the statements
class A
{
public int P {
set {...}
}
}
class B: A
{
new public int P {
get {...}
}
}
the assignment to b.P causes a compile-time error to be reported, since the read-only P property in B hides the write-only P property in A. Note, however, that a cast can be used to access the hidden P property. end example]
B b = new B();
b.P = 1; // Error, B.P is read-only
((A)b).P = 1; // Ok, reference to A.P
Unlike public fields, properties provide a separation between an object's internal state and its public interface.
class Label
{
private int x, y;
private string caption;
public Label(int x, int y, string caption) {
this.x = x;
this.y = y;
this.caption = caption;
}
public int X {
get { return x; }
}
public int Y {
get { return y; }
}
public Point Location {
get { return new Point(x, y); }
}
public string Caption {
get { return caption; }
}
}
class Label
{
private Point location;
private string caption;
public Label(int x, int y, string caption) {
this.location = new Point(x, y);
this.caption = caption;
}
public int X {
get { return location.x; }
}
public int Y {
get { return location.y; }
}
public Point Location {
get { return location; }
}
public string Caption {
get { return caption; }
}
}
the value of the Next property depends on the number of times the property has previously been accessed.
class Counter
{
private int next;
public int Next {
get { return next++; }
}
}
Properties can be used to delay initialization of a resource until the moment it is first referenced.
using System.IO;
public class Console
{
private static TextReader reader;
private static TextWriter writer;
private static TextWriter error;
public static TextReader In {
get {
if (reader == null) {
reader = new StreamReader(Console.OpenStandardInput());
}
return reader;
}
}
public static TextWriter Out {
get {
if (writer == null) {
writer = new StreamWriter(Console.OpenStandardOutput());
}
return writer;
}
}
public static TextWriter Error {
get {
if (error == null) {
error = new StreamWriter(Console.OpenStandardError());
}
return error;
}
}
}
the underlying TextWriter for the output device is created. But if the application makes no reference to the In and Error properties, then no objects are created for those devices. end example]
Console.Out.WriteLine("hello, world");