2010-03-30 Jb Evain <jbevain@novell.com>
[mcs.git] / docs / ecma334 / 17.6.2.xml
blob15eef411d3b8d0a6f419c560c0b1f0f50ca10707
1 <?xml version="1.0"?>
2 <clause number="17.6.2" title="Accessors">
3   <paragraph>The <non_terminal where="17.6.2">accessor-declarations</non_terminal> of a property specify the executable statements associated with reading and writing that property. <grammar_production><name><non_terminal where="17.6.2">accessor-declarations</non_terminal></name> : <rhs><non_terminal where="17.6.2">get-accessor-declaration</non_terminal><non_terminal where="17.6.2">set-accessor-declaration</non_terminal><opt/></rhs><rhs><non_terminal where="17.6.2">set-accessor-declaration</non_terminal><non_terminal where="17.6.2">get-accessor-declaration</non_terminal><opt/></rhs></grammar_production><grammar_production><name><non_terminal where="17.6.2">get-accessor-declaration</non_terminal></name> : <rhs><non_terminal where="24.2">attributes</non_terminal><opt/><terminal>get</terminal><non_terminal where="17.6.2">accessor-body</non_terminal></rhs></grammar_production><grammar_production><name><non_terminal where="17.6.2">set-accessor-declaration</non_terminal></name> : <rhs><non_terminal where="24.2">attributes</non_terminal><opt/><terminal>set</terminal><non_terminal where="17.6.2">accessor-body</non_terminal></rhs></grammar_production><grammar_production><name><non_terminal where="17.6.2">accessor-body</non_terminal></name> : <rhs><non_terminal where="15.2">block</non_terminal></rhs><rhs><terminal>;</terminal></rhs></grammar_production></paragraph>
4   <paragraph>The accessor declarations consist of a <non_terminal where="17.6.2">get-accessor-declaration</non_terminal>, a <non_terminal where="17.6.2">set-accessor-declaration</non_terminal>, or both. Each accessor declaration consists of the token get or set followed by an <non_terminal where="17.6.2">accessor-body</non_terminal>. For abstract and extern properties, the <non_terminal where="17.6.2">accessor-body</non_terminal> for each accessor specified is simply a semicolon. For the accessors of any non-abstract, non-extern property, the <non_terminal where="17.6.2">accessor-body</non_terminal> is a block which specifies the statements to be executed when the corresponding accessor is invoked. </paragraph>
5   <paragraph>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 (<hyperlink>14.1.1</hyperlink>). The body of a get accessor must conform to the rules for value-returning methods described in <hyperlink>17.5.8</hyperlink>. 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. </paragraph>
6   <paragraph>A set accessor corresponds to a method with a single value parameter of the property type and a <keyword>void</keyword> return type. The implicit parameter of a set accessor is always named value. When a property is referenced as the target of an assignment (<hyperlink>14.13</hyperlink>), or as the operand of ++ or  (<hyperlink>14.5.9</hyperlink>, --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 (<hyperlink>14.13.1</hyperlink>). The body of a set accessor must conform to the rules for <keyword>void</keyword> methods described in <hyperlink>17.5.8</hyperlink>. 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. </paragraph>
7   <paragraph>Based on the presence or absence of the get and set accessors, a property is classified as follows: <list><list_item> A property that includes both a get accessor and a set accessor is said to be a read-write property. </list_item><list_item> A property that has only a get accessor is said to be a read-only property. It is a compile-time error for a read-only property to be the target of an assignment. </list_item><list_item> A property that has only a set accessor is said to be a write-only property. Except as the target of an assignment, it is a compile-time error to reference a write-only property in an expression. <note>[Note: The  pre-and postfix ++ and  --operators cannot be applied to write-only properties, since these operators read the old value of their operand before they write the new one. end note]</note> </list_item></list></paragraph>
8   <paragraph>
9     <example>[Example: In the example <code_example><![CDATA[
10 public class Button: Control  
11 {  
12    private string caption;  
13    public string Caption {  
14       get {  
15          return caption;  
16       }  
17       set {  
18          if (caption != value) {  
19             caption = value;  
20             Repaint();  
21          }  
22       }  
23    }  
24    public override void Paint(Graphics g, Rectangle r) {  
25       // Painting code goes here  
26    }  
27 }  
28 ]]></code_example>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. </example>
29   </paragraph>
30   <paragraph>
31     <example>Given the Button class above, the following is an example of use of the Caption property: <code_example><![CDATA[
32 Button okButton = new Button();  
33 okButton.Caption = "OK";      // Invokes set accessor  
34 string s = okButton.Caption;    // Invokes get accessor  
35 ]]></code_example></example>
36   </paragraph>
37   <paragraph>
38     <example>Here, the set accessor is invoked by assigning a value to the property, and the get accessor is invoked by referencing the property in an expression. end example]</example>
39   </paragraph>
40   <paragraph>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. <note>[Note: As such, it is not possible for the two accessors of a read-write property to have different accessibility. end note]</note> <example>[Example: The example <code_example><![CDATA[
41 class A  
42 {  
43    private string name;  
44    public string Name {       // Error, duplicate member name  
45       get { return name; }  
46    }  
47    public string Name {       // Error, duplicate member name  
48       set { name = value; }  
49    }  
50 }  
51 ]]></code_example>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]</example> </paragraph>
52   <paragraph>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. <example>[Example: In the example <code_example><![CDATA[
53 class A  
54 {  
55    public int P {  
56       set {...}  
57    }  
58 }  
59 class B: A  
60 {  
61    new public int P {  
62       get {...}  
63    }  
64 }  
65 ]]></code_example>the P property in B hides the P property in A with respect to both reading and writing. Thus, in the statements <code_example><![CDATA[
66 B b = new B();  
67 b.P = 1;     // Error, B.P is read-only  
68 ((A)b).P = 1;  // Ok, reference to A.P  
69 ]]></code_example>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]</example> </paragraph>
70   <paragraph>Unlike public fields, properties provide a separation between an object's internal state and its public interface. <example>[Example: Consider the example: <code_example><![CDATA[
71 class Label  
72 {  
73    private int x, y;  
74    private string caption;  
75    public Label(int x, int y, string caption) {  
76       this.x = x;  
77       this.y = y;  
78       this.caption = caption;  
79    }  
80    public int X {  
81       get { return x; }  
82    }  
83    public int Y {  
84       get { return y; }  
85    }  
86    public Point Location {  
87       get { return new Point(x, y); }  
88    }  
89    public string Caption {  
90       get { return caption; }  
91    }  
92 }  
93 ]]></code_example></example></paragraph>
94   <paragraph>
95     <example>Here, the Label class uses two <keyword>int</keyword> fields, x and y, to store its location. The location is publicly exposed both as an X and a Y property and as a Location property of type Point. If, in a future version of Label, it becomes more convenient to store the location as a Point internally, the change can be made without affecting the public interface of the class: <code_example><![CDATA[
96 class Label  
97 {  
98    private Point location;  
99    private string caption;  
100    public Label(int x, int y, string caption) {  
101       this.location = new Point(x, y);  
102       this.caption = caption;  
103    }  
104    public int X {  
105       get { return location.x; }  
106    }  
107    public int Y {  
108       get { return location.y; }  
109    }  
110    public Point Location {  
111       get { return location; }  
112    }  
113    public string Caption {  
114       get { return caption; }  
115    }  
116 }  
117 ]]></code_example></example>
118   </paragraph>
119   <paragraph>
120     <example>Had x and y instead been public readonly fields, it would have been impossible to make such a change to the Label class. end example]</example>
121   </paragraph>
122   <paragraph>
123     <note>[Note: Exposing state through properties is not necessarily any less efficient than exposing fields directly. In particular, when a property is non-virtual and contains only a small amount of code, the execution environment may replace calls to accessors with the actual code of the accessors. This process is known as inlining, and it makes property access as efficient as field access, yet preserves the increased flexibility of properties. end note]</note>
124   </paragraph>
125   <paragraph>
126     <example>[Example: Since invoking a get accessor is conceptually equivalent to reading the value of a field, it is considered bad programming style for get accessors to have observable side-effects. In the example <code_example><![CDATA[
127 class Counter  
128 {  
129    private int next;  
130    public int Next {  
131       get { return next++; }  
132    }  
133 }  
134 ]]></code_example>the value of the Next property depends on the number of times the property has previously been accessed. </example>
135   </paragraph>
136   <paragraph>
137     <example>Thus, accessing the property produces an observable side effect, and the property should be implemented as a method instead. end example]</example>
138   </paragraph>
139   <paragraph>
140     <note>[Note: The &quot;no side-effects&quot; convention for get accessors doesn't mean that get accessors should always be written to simply return values stored in fields. Indeed, get accessors often compute the value of a property by accessing multiple fields or invoking methods. However, a properly designed get accessor performs no actions that cause observable changes in the state of the object. end note]</note>
141   </paragraph>
142   <paragraph>Properties can be used to delay initialization of a resource until the moment it is first referenced. <example>[Example: For example: <code_example><![CDATA[
143 using System.IO;  
144 public class Console  
145 {  
146    private static TextReader reader;  
147    private static TextWriter writer;  
148    private static TextWriter error;  
149    public static TextReader In {  
150       get {  
151          if (reader == null) {  
152             reader = new StreamReader(Console.OpenStandardInput());  
153          }  
154          return reader;  
155       }  
156    }  
157    public static TextWriter Out {  
158       get {  
159          if (writer == null) {  
160             writer = new StreamWriter(Console.OpenStandardOutput());  
161          }  
162          return writer;  
163       }  
164    }  
165    public static TextWriter Error {  
166       get {  
167          if (error == null) {  
168             error = new StreamWriter(Console.OpenStandardError());  
169          }  
170          return error;  
171       }  
172    }  
173 }  
174 ]]></code_example></example></paragraph>
175   <paragraph>
176     <example>The Console class contains three properties, In, Out, and Error, that represent the standard input, output, and error devices, respectively. By exposing these members as properties, the Console class can delay their initialization until they are actually used. For example, upon first referencing the Out property, as in <code_example><![CDATA[
177 Console.Out.WriteLine("hello, world");  
178 ]]></code_example>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]</example>
179   </paragraph>
180 </clause>