
- •Foreword
- •Introduction
- •Scope
- •Conformance
- •Normative references
- •Definitions
- •Notational conventions
- •Acronyms and abbreviations
- •General description
- •Language overview
- •Getting started
- •Types
- •Predefined types
- •Conversions
- •Array types
- •Type system unification
- •Variables and parameters
- •Automatic memory management
- •Expressions
- •Statements
- •Classes
- •Constants
- •Fields
- •Methods
- •Properties
- •Events
- •Operators
- •Indexers
- •Instance constructors
- •Destructors
- •Static constructors
- •Inheritance
- •Static classes
- •Partial type declarations
- •Structs
- •Interfaces
- •Delegates
- •Enums
- •Namespaces and assemblies
- •Versioning
- •Extern Aliases
- •Attributes
- •Generics
- •Why generics?
- •Creating and consuming generics
- •Multiple type parameters
- •Constraints
- •Generic methods
- •Anonymous methods
- •Iterators
- •Lexical structure
- •Programs
- •Grammars
- •Lexical grammar
- •Syntactic grammar
- •Grammar ambiguities
- •Lexical analysis
- •Line terminators
- •Comments
- •White space
- •Tokens
- •Unicode escape sequences
- •Identifiers
- •Keywords
- •Literals
- •Boolean literals
- •Integer literals
- •Real literals
- •Character literals
- •String literals
- •The null literal
- •Operators and punctuators
- •Pre-processing directives
- •Conditional compilation symbols
- •Pre-processing expressions
- •Declaration directives
- •Conditional compilation directives
- •Diagnostic directives
- •Region control
- •Line directives
- •Pragma directives
- •Basic concepts
- •Application startup
- •Application termination
- •Declarations
- •Members
- •Namespace members
- •Struct members
- •Enumeration members
- •Class members
- •Interface members
- •Array members
- •Delegate members
- •Member access
- •Declared accessibility
- •Accessibility domains
- •Protected access for instance members
- •Accessibility constraints
- •Signatures and overloading
- •Scopes
- •Name hiding
- •Hiding through nesting
- •Hiding through inheritance
- •Namespace and type names
- •Unqualified name
- •Fully qualified names
- •Automatic memory management
- •Execution order
- •Types
- •Value types
- •The System.ValueType type
- •Default constructors
- •Struct types
- •Simple types
- •Integral types
- •Floating point types
- •The decimal type
- •The bool type
- •Enumeration types
- •Reference types
- •Class types
- •The object type
- •The string type
- •Interface types
- •Array types
- •Delegate types
- •Boxing and unboxing
- •Boxing conversions
- •Unboxing conversions
- •Variables
- •Variable categories
- •Static variables
- •Instance variables
- •Instance variables in classes
- •Instance variables in structs
- •Array elements
- •Value parameters
- •Reference parameters
- •Output parameters
- •Local variables
- •Default values
- •Definite assignment
- •Initially assigned variables
- •Initially unassigned variables
- •Precise rules for determining definite assignment
- •General rules for statements
- •Block statements, checked, and unchecked statements
- •Expression statements
- •Declaration statements
- •If statements
- •Switch statements
- •While statements
- •Do statements
- •For statements
- •Break, continue, and goto statements
- •Throw statements
- •Return statements
- •Try-catch statements
- •Try-finally statements
- •Try-catch-finally statements
- •Foreach statements
- •Using statements
- •Lock statements
- •General rules for simple expressions
- •General rules for expressions with embedded expressions
- •Invocation expressions and object creation expressions
- •Simple assignment expressions
- •&& expressions
- •|| expressions
- •! expressions
- •?: expressions
- •Anonymous method expressions
- •Yield statements
- •Variable references
- •Atomicity of variable references
- •Conversions
- •Implicit conversions
- •Identity conversion
- •Implicit numeric conversions
- •Implicit enumeration conversions
- •Implicit reference conversions
- •Boxing conversions
- •Implicit type parameter conversions
- •Implicit constant expression conversions
- •User-defined implicit conversions
- •Explicit conversions
- •Explicit numeric conversions
- •Explicit enumeration conversions
- •Explicit reference conversions
- •Unboxing conversions
- •User-defined explicit conversions
- •Standard conversions
- •Standard implicit conversions
- •Standard explicit conversions
- •User-defined conversions
- •Permitted user-defined conversions
- •Evaluation of user-defined conversions
- •User-defined implicit conversions
- •User-defined explicit conversions
- •Anonymous method conversions
- •Method group conversions
- •Expressions
- •Expression classifications
- •Values of expressions
- •Operators
- •Operator precedence and associativity
- •Operator overloading
- •Unary operator overload resolution
- •Binary operator overload resolution
- •Candidate user-defined operators
- •Numeric promotions
- •Unary numeric promotions
- •Binary numeric promotions
- •Member lookup
- •Base types
- •Function members
- •Argument lists
- •Overload resolution
- •Applicable function member
- •Better function member
- •Better conversion
- •Function member invocation
- •Invocations on boxed instances
- •Primary expressions
- •Literals
- •Simple names
- •Invariant meaning in blocks
- •Parenthesized expressions
- •Member access
- •Identical simple names and type names
- •Invocation expressions
- •Method invocations
- •Delegate invocations
- •Element access
- •Array access
- •Indexer access
- •This access
- •Base access
- •Postfix increment and decrement operators
- •The new operator
- •Object creation expressions
- •Array creation expressions
- •Delegate creation expressions
- •The typeof operator
- •The checked and unchecked operators
- •Default value expression
- •Anonymous methods
- •Anonymous method signatures
- •Anonymous method blocks
- •Outer variables
- •Captured outer variables
- •Instantiation of local variables
- •Anonymous method evaluation
- •Implementation example
- •Unary expressions
- •Unary plus operator
- •Unary minus operator
- •Logical negation operator
- •Bitwise complement operator
- •Prefix increment and decrement operators
- •Cast expressions
- •Arithmetic operators
- •Multiplication operator
- •Division operator
- •Remainder operator
- •Addition operator
- •Subtraction operator
- •Shift operators
- •Relational and type-testing operators
- •Integer comparison operators
- •Floating-point comparison operators
- •Decimal comparison operators
- •Boolean equality operators
- •Enumeration comparison operators
- •Reference type equality operators
- •String equality operators
- •Delegate equality operators
- •The is operator
- •The as operator
- •Logical operators
- •Integer logical operators
- •Enumeration logical operators
- •Boolean logical operators
- •Conditional logical operators
- •Boolean conditional logical operators
- •User-defined conditional logical operators
- •Conditional operator
- •Assignment operators
- •Simple assignment
- •Compound assignment
- •Event assignment
- •Expression
- •Constant expressions
- •Boolean expressions
- •Statements
- •End points and reachability
- •Blocks
- •Statement lists
- •The empty statement
- •Labeled statements
- •Declaration statements
- •Local variable declarations
- •Local constant declarations
- •Expression statements
- •Selection statements
- •The if statement
- •The switch statement
- •Iteration statements
- •The while statement
- •The do statement
- •The for statement
- •The foreach statement
- •Jump statements
- •The break statement
- •The continue statement
- •The goto statement
- •The return statement
- •The throw statement
- •The try statement
- •The checked and unchecked statements
- •The lock statement
- •The using statement
- •The yield statement
- •Namespaces
- •Compilation units
- •Namespace declarations
- •Extern alias directives
- •Using directives
- •Using alias directives
- •Using namespace directives
- •Namespace members
- •Type declarations
- •Qualified alias member
- •Classes
- •Class declarations
- •Class modifiers
- •Abstract classes
- •Sealed classes
- •Static classes
- •Class base specification
- •Base classes
- •Interface implementations
- •Class body
- •Partial declarations
- •Class members
- •Inheritance
- •The new modifier
- •Access modifiers
- •Constituent types
- •Static and instance members
- •Nested types
- •Fully qualified name
- •Declared accessibility
- •Hiding
- •this access
- •Reserved member names
- •Member names reserved for properties
- •Member names reserved for events
- •Member names reserved for indexers
- •Member names reserved for destructors
- •Constants
- •Fields
- •Static and instance fields
- •Readonly fields
- •Using static readonly fields for constants
- •Versioning of constants and static readonly fields
- •Volatile fields
- •Field initialization
- •Variable initializers
- •Static field initialization
- •Instance field initialization
- •Methods
- •Method parameters
- •Value parameters
- •Reference parameters
- •Output parameters
- •Parameter arrays
- •Static and instance methods
- •Virtual methods
- •Override methods
- •Sealed methods
- •Abstract methods
- •External methods
- •Method body
- •Method overloading
- •Properties
- •Static and instance properties
- •Accessors
- •Virtual, sealed, override, and abstract accessors
- •Events
- •Field-like events
- •Event accessors
- •Static and instance events
- •Virtual, sealed, override, and abstract accessors
- •Indexers
- •Indexer overloading
- •Operators
- •Unary operators
- •Binary operators
- •Conversion operators
- •Instance constructors
- •Constructor initializers
- •Instance variable initializers
- •Constructor execution
- •Default constructors
- •Private constructors
- •Optional instance constructor parameters
- •Static constructors
- •Destructors
- •Structs
- •Struct declarations
- •Struct modifiers
- •Struct interfaces
- •Struct body
- •Struct members
- •Class and struct differences
- •Value semantics
- •Inheritance
- •Assignment
- •Default values
- •Boxing and unboxing
- •Meaning of this
- •Field initializers
- •Constructors
- •Destructors
- •Static constructors
- •Struct examples
- •Database integer type
- •Database boolean type
- •Arrays
- •Array types
- •The System.Array type
- •Array creation
- •Array element access
- •Array members
- •Array covariance
- •Arrays and the generic IList interface
- •Array initializers
- •Interfaces
- •Interface declarations
- •Interface modifiers
- •Base interfaces
- •Interface body
- •Interface members
- •Interface methods
- •Interface properties
- •Interface events
- •Interface indexers
- •Interface member access
- •Fully qualified interface member names
- •Interface implementations
- •Explicit interface member implementations
- •Interface mapping
- •Interface implementation inheritance
- •Interface re-implementation
- •Abstract classes and interfaces
- •Enums
- •Enum declarations
- •Enum modifiers
- •Enum members
- •The System.Enum type
- •Enum values and operations
- •Delegates
- •Delegate declarations
- •Delegate instantiation
- •Delegate invocation
- •Exceptions
- •Causes of exceptions
- •The System.Exception class
- •How exceptions are handled
- •Common Exception Classes
- •Attributes
- •Attribute classes
- •Attribute usage
- •Positional and named parameters
- •Attribute parameter types
- •Attribute specification
- •Attribute instances
- •Compilation of an attribute
- •Run-time retrieval of an attribute instance
- •Reserved attributes
- •The AttributeUsage attribute
- •The Conditional attribute
- •Conditional Methods
- •Conditional Attribute Classes
- •The Obsolete attribute
- •Unsafe code
- •Unsafe contexts
- •Pointer types
- •Fixed and moveable variables
- •Pointer conversions
- •Pointers in expressions
- •Pointer indirection
- •Pointer member access
- •Pointer element access
- •The address-of operator
- •Pointer increment and decrement
- •Pointer arithmetic
- •Pointer comparison
- •The sizeof operator
- •The fixed statement
- •Stack allocation
- •Dynamic memory allocation
- •Generics
- •Generic class declarations
- •Type parameters
- •The instance type
- •Members of generic classes
- •Static fields in generic classes
- •Static constructors in generic classes
- •Accessing protected members
- •Overloading in generic classes
- •Parameter array methods and type parameters
- •Overriding and generic classes
- •Operators in generic classes
- •Nested types in generic classes
- •Generic struct declarations
- •Generic interface declarations
- •Uniqueness of implemented interfaces
- •Explicit interface member implementations
- •Generic delegate declarations
- •Constructed types
- •Type arguments
- •Open and closed types
- •Base classes and interfaces of a constructed type
- •Members of a constructed type
- •Accessibility of a constructed type
- •Conversions
- •Using alias directives
- •Generic methods
- •Generic method signatures
- •Virtual generic methods
- •Calling generic methods
- •Inference of type arguments
- •Using a generic method with a delegate
- •Constraints
- •Satisfying constraints
- •Member lookup on type parameters
- •Type parameters and boxing
- •Conversions involving type parameters
- •Iterators
- •Iterator blocks
- •Enumerator interfaces
- •Enumerable interfaces
- •Yield type
- •This access
- •Enumerator objects
- •The MoveNext method
- •The Current property
- •The Dispose method
- •Enumerable objects
- •The GetEnumerator method
- •Implementation example
- •Lexical grammar
- •Line terminators
- •White space
- •Comments
- •Unicode character escape sequences
- •Identifiers
- •Keywords
- •Literals
- •Operators and punctuators
- •Pre-processing directives
- •Syntactic grammar
- •Basic concepts
- •Types
- •Expressions
- •Statements
- •Classes
- •Structs
- •Arrays
- •Interfaces
- •Enums
- •Delegates
- •Attributes
- •Generics
- •Grammar extensions for unsafe code
- •Undefined behavior
- •Implementation-defined behavior
- •Unspecified behavior
- •Other Issues
- •Capitalization styles
- •Pascal casing
- •Camel casing
- •All uppercase
- •Capitalization summary
- •Word choice
- •Namespaces
- •Classes
- •Interfaces
- •Enums
- •Static fields
- •Parameters
- •Methods
- •Properties
- •Events
- •Case sensitivity
- •Avoiding type name confusion
- •Documentation Comments
- •Introduction
- •Recommended tags
- •<code>
- •<example>
- •<exception>
- •<list>
- •<para>
- •<param>
- •<paramref>
- •<permission>
- •<remarks>
- •<returns>
- •<seealso>
- •<summary>
- •<value>
- •Processing the documentation file
- •ID string format
- •ID string examples
- •An example
- •C# source code
- •Resulting XML
|
C# LANGUAGE SPECIFICATION |
1 |
[DllImport("kernel32", SetLastError=true)] |
2 |
static extern int GetCurrentDirectory(int bufSize, StringBuilder buf); |
3 |
[DllImport("kernel32", SetLastError=true)] |
4static extern bool SetCurrentDirectory(string name);
5}
6end example]
717.5.8 Method body
8The method-body of a method declaration consists of either a block or a semicolon.
9Abstract and external method declarations do not provide a method implementation, so their method bodies
10simply consist of a semicolon. For any other method, the method body is a block (§15.2) that contains the
11statements to execute when that method is invoked.
12When the return type of a method is void, return statements (§15.9.4) in that method’s body are not
13permitted to specify an expression. If execution of the method body of a void method completes normally
14(that is, control flows off the end of the method body), that method simply returns to its caller.
15When the return type of a method is not void, each return statement in that method body shall specify an
16expression of a type that is implicitly convertible to the return type. The endpoint of the method body of a
17value-returning method shall not be reachable. In other words, in a value-returning method, control is not
18permitted to flow off the end of the method body.
19[Example: In the following code
20class A
21{
22 |
public int F() {} |
// Error, return value required |
23 |
public int G() { |
|
24 |
return 1; |
|
25 |
} |
|
26 |
public int H(bool b) { |
|
27 |
if (b) { |
|
28 |
return 1; |
|
29 |
} |
|
30 |
else { |
|
31 |
return 0; |
|
32 |
} |
|
33}
34}
35the value-returning F method results in a compile-time error because control can flow off the end of the
36method body. The G and H methods are correct because all possible execution paths end in a return
37statement that specifies a return value. end example]
3817.5.9 Method overloading
39The method overload resolution rules are described in §14.4.2.
4017.6 Properties
41A property is a member that provides access to a characteristic of an object or a class. Examples of
42properties include the length of a string, the size of a font, the caption of a window, the name of a customer,
43and so on. Properties are a natural extension of fields—both are named members with associated types, and
44the syntax for accessing fields and properties is the same. However, unlike fields, properties do not denote
45storage locations. Instead, properties have accessors that specify the statements to be executed when their
46values are read or written. Properties thus provide a mechanism for associating actions with the reading and
47writing of an object’s characteristics; furthermore, they permit such characteristics to be computed.
48Properties are declared using property-declarations:
290
|
Chapter 17 Classes |
1 |
property-declaration: |
2 |
attributesopt property-modifiersopt type member-name { accessor-declarations } |
3 |
property-modifiers: |
4 |
property-modifier |
5 |
property-modifiers property-modifier |
6 |
property-modifier: |
7 |
new |
8 |
public |
9 |
protected |
10 |
internal |
11 |
private |
12 |
static |
13 |
virtual |
14 |
sealed |
15 |
override |
16 |
abstract |
17extern
18member-name:
19 |
identifier |
20 |
interface-type . identifier |
21A property-declaration can include a set of attributes (§24) and a valid combination of the four access
22modifiers (§17.2.3), the new (§17.2.2), static (§17.6.1), virtual (§17.5.3, §17.6.3), override (§17.5.4,
23§17.6.3), sealed (§17.5.5), abstract (§17.5.6, §17.6.3), and extern modifiers.
24Property declarations are subject to the same rules as method declarations (§17.5) with regard to valid
25combinations of modifiers.
26The type of a property declaration specifies the type of the property introduced by the declaration, and the
27member-name specifies the name of the property. Unless the property is an explicit interface member
28implementation, the member-name is simply an identifier. For an explicit interface member implementation
29(§20.4.1), the member-name consists of an interface-type followed by a “.” and an identifier.
30The type of a property shall be at least as accessible as the property itself (§10.5.4).
31The accessor-declarations, which shall be enclosed in “{” and “}” tokens, declare the accessors (§17.6.2) of
32the property. The accessors specify the executable statements associated with reading and writing the
33property.
34Even though the syntax for accessing a property is the same as that for a field, a property is not classified as
35a variable. Thus, it is not possible to pass a property as a ref or out argument.
36When a property declaration includes an extern modifier, the property is said to be an external property.
37Because an external property declaration provides no actual implementation, each of its accessor-
38declarations consists of a semicolon.
3917.6.1 Static and instance properties
40When a property declaration includes a static modifier, the property is said to be a static property. When
41no static modifier is present, the property is said to be an instance property.
42A static property is not associated with a specific instance, and it is a compile-time error to refer to this in
43the accessors of a static property.
44An instance property is associated with a given instance of a class, and that instance can be accessed as
45this (§14.5.7) in the accessors of that property.
46The differences between static and instance members are discussed further in §17.2.5.
291
C# LANGUAGE SPECIFICATION
117.6.2 Accessors
2The accessor-declarations of a property specify the executable statements associated with reading and
3writing that property.
4accessor-declarations:
5 |
get-accessor-declaration set-accessor-declarationopt |
||
6 |
set-accessor-declaration get-accessor-declarationopt |
||
7 |
get-accessor-declaration: |
|
|
8 |
attributesopt accessor-modifieropt |
get |
accessor-body |
9 |
set-accessor-declaration: |
|
|
10 |
attributesopt accessor-modifieropt |
set |
accessor-body |
11 |
accessor-modifier: |
|
|
12 |
protected |
|
|
13 |
internal |
|
|
14 |
private |
|
|
15 |
protected internal |
|
|
16internal protected
17accessor-body:
18 |
block |
19;
20The accessor declarations consist of a get-accessor-declaration, a set-accessor-declaration, or both. Each
21accessor declaration consists of the token get or set followed by an accessor-body. For abstract and
22extern properties, the accessor-body for each accessor specified is simply a semicolon. For the accessors
23of any non-abstract, non-extern property, the accessor-body is a block which specifies the statements to be
24executed when the corresponding accessor is invoked.
25A get accessor corresponds to a parameterless method with a return value of the property type. Except as
26the target of an assignment, when a property is referenced in an expression, the get accessor of the property
27is invoked to compute the value of the property (§14.1.1). The body of a get accessor shall conform to the
28rules for value-returning methods described in §17.5.8. In particular, all return statements in the body of a
29get accessor shall specify an expression that is implicitly convertible to the property type. Furthermore, the
30endpoint of a get accessor shall not be reachable.
31A set accessor corresponds to a method with a single value parameter of the property type and a void
32return type. The implicit parameter of a set accessor is always named value. When a property is
33referenced as the target of an assignment (§14.13), or as the operand of ++ or –- (§14.5.9, 14.6.5), the set
34accessor is invoked with an argument that provides the new value (§14.13.1). The body of a set accessor
35shall conform to the rules for void methods described in §17.5.8. In particular, return statements in the
36set accessor body are not permitted to specify an expression. Since a set accessor implicitly has a
37parameter named value, it is a compile-time error for a local variable declaration or a local constant
38declaration in a set accessor to have that name.
39Based on the presence or absence of the get and set accessors, a property is classified as follows:
40• A property that includes both a get accessor and a set accessor is said to be a read-write property.
41• A property that has only a get accessor is said to be a read-only property. It is a compile-time error for
42a read-only property to be the target of an assignment.
43• A property that has only a set accessor is said to be a write-only property. Except as the target of a
44simple assignment, it is a compile-time error to reference a write-only property in an expression. [Note:
45The preand postfix ++ and -- operators and compound assignment operators cannot be applied to
46write-only properties, since these operators read the old value of their operand before they write the new
47one. end note]
48The use of accessor-modifiers is governed by the following restrictions:
292
Chapter 17 Classes
1• An accessor-modifier cannot be used in an interface or in an explicit interface member implementation.
2• For a property or indexer that has no override modifer, an accessor-modifier is permitted only if the
3property or indexer has both a get and set accessor, and then is permitted only on one of those
4accessors.
5• For a property or indexer that includes an override modifer, the accessor-modifier of an accessor shall
6match the accessor-modifier, if any, of the accessor being overridden.
7• The accessor-modifier shall declare an accessibility that is strictly more restrictive than the declared
8accessibility of the property or indexer itself. To be precise:
9o If the property or indexer has a declared accessibility of public, any accessor-modifier can be used.
10o If the property or indexer has a declared accessibility of protected internal, the accessor-modifier can
11be internal, protected, or private.
12o If the property or indexer has a declared accessibility of internal or protected, the accessor-modifier
13shall be private.
14o If the property or indexer has a declared accessibility of private, no accessor-modifier shall be used.
15If an accessor has an accessor-modifier, the accessibility domain (§10.5.2) of the accessor is determined
16using the declared accessibility of the accessor-modifier. If an accessor does not have an accessor-modifier,
17the accessibility domain of the accessor is determined from the declared accessibility of the property or
18indexer.
19[Example: In the following code
20public class Button: Control
21{
22 |
private string caption; |
23 |
public string Caption { |
24 |
get { |
25 |
return caption; |
26 |
} |
27 |
set { |
28 |
if (caption != value) { |
29 |
caption = value; |
30 |
Repaint(); |
31 |
} |
32 |
} |
33 |
} |
34 |
public override void Paint(Graphics g, Rectangle r) { |
35 |
// Painting code goes here |
36}
37}
38the Button control declares a public Caption property. The get accessor of the Caption property returns
39the string stored in the private caption field. The set accessor checks if the new value is different from the
40current value, and if so, it stores the new value and repaints the control. Properties often follow the pattern
41shown above: The get accessor simply returns a value stored in a private field, and the set accessor
42modifies that private field and then performs any additional actions required to fully update the state of the
43object.
44Given the Button class above, the following is an example of use of the Caption property:
45Button okButton = new Button();
46 |
okButton.Caption = "OK"; |
// |
Invokes |
set |
accessor |
47 |
string s = okButton.Caption; |
// |
Invokes |
get |
accessor |
48Here, the set accessor is invoked by assigning a value to the property, and the get accessor is invoked by
49referencing the property in an expression. end example]
50The get and set accessors of a property are not distinct members, and it is not possible to declare the
51accessors of a property separately. [Example: The example
293
C# LANGUAGE SPECIFICATION
1class A
2{
3 |
private string name; |
|
4 |
public string Name { |
// Error, duplicate member name |
5 |
get { return name; } |
|
6 |
} |
|
7 |
public string Name { |
// Error, duplicate member name |
8 |
set { name = value; } |
|
9}
10}
11does not declare a single read-write property. Rather, it declares two properties with the same name, one
12read-only and one write-only. Since two members declared in the same class cannot have the same name, the
13example causes a compile-time error to occur. end example]
14When a derived class declares a property by the same name as an inherited property, the derived property
15hides the inherited property with respect to both reading and writing. [Example: In the following code
16class A
17{
18 |
public |
int P { |
19 |
set |
{…} |
20}
21}
22class B: A
23{
24 |
new public int P { |
25 |
get {…} |
26}
27}
28the P property in B hides the P property in A with respect to both reading and writing. Thus, in the
29statements
30B b = new B();
31b.P = 1; // Error, B.P is read-only
32((A)b).P = 1; // Ok, reference to A.P
33the assignment to b.P causes a compile-time error to be reported, since the read-only P property in B hides
34the write-only P property in A. Note, however, that a cast can be used to access the hidden P property. end
35example]
36Unlike public fields, properties provide a separation between an object’s internal state and its public
37interface. [Example: Consider the example:
38class Label
39{
40 |
private int x, y; |
41 |
private string caption; |
42 |
public Label(int x, int y, string caption) { |
43 |
this.x = x; |
44 |
this.y = y; |
45 |
this.caption = caption; |
46 |
} |
47 |
public int X { |
48 |
get { return x; } |
49 |
} |
50 |
public int Y { |
51 |
get { return y; } |
52 |
} |
53 |
public Point Location { |
54 |
get { return new Point(x, y); } |
55 |
} |
294
|
Chapter 17 Classes |
1 |
public string Caption { |
2 |
get { return caption; } |
3}
4}
5Here, the Label class uses two int fields, x and y, to store its location. The location is publicly exposed
6both as an X and a Y property and as a Location property of type Point. If, in a future version of Label,
7it becomes more convenient to store the location as a Point internally, the change can be made without
8affecting the public interface of the class:
9class Label
10{
11 |
private Point location; |
12 |
private string caption; |
13 |
public Label(int x, int y, string caption) { |
14 |
this.location = new Point(x, y); |
15 |
this.caption = caption; |
16 |
} |
17 |
public int X { |
18 |
get { return location.x; } |
19 |
} |
20 |
public int Y { |
21 |
get { return location.y; } |
22 |
} |
23 |
public Point Location { |
24 |
get { return location; } |
25 |
} |
26 |
public string Caption { |
27 |
get { return caption; } |
28}
29}
30Had x and y instead been public readonly fields, it would have been impossible to make such a change
31to the Label class. end example]
32[Note: Exposing state through properties is not necessarily any less efficient than exposing fields directly. In
33particular, when a property is non-virtual and contains only a small amount of code, the execution
34environment might replace calls to accessors with the actual code of the accessors. This process is known as
35inlining, and it makes property access as efficient as field access, yet preserves the increased flexibility of
36properties. end note]
37[Example: Since invoking a get accessor is conceptually equivalent to reading the value of a field, it is
38considered bad programming style for get accessors to have observable side-effects. In the example
39class Counter
40{
41 |
private int next; |
42 |
public int Next { |
43 |
get { return next++; } |
44}
45}
46the value of the Next property depends on the number of times the property has previously been accessed.
47Thus, accessing the property produces an observable side effect, and the property should be implemented as
48a method instead. end example]
49[Note: The “no side-effects” convention for get accessors doesn’t mean that get accessors should always
50be written to simply return values stored in fields. Indeed, get accessors often compute the value of a
51property by accessing multiple fields or invoking methods. However, a properly designed get accessor
52performs no actions that cause observable changes in the state of the object. end note]
53Properties can be used to delay initialization of a resource until the moment it is first referenced. [Example:
54using System.IO;
295
C# LANGUAGE SPECIFICATION
1public class Console
2{
3 |
private static TextReader reader; |
4 |
private static TextWriter writer; |
5 |
private static TextWriter error; |
6 |
public static TextReader In { |
7 |
get { |
8 |
if (reader == null) { |
9 |
reader = new StreamReader(Console.OpenStandardInput()); |
10 |
} |
11 |
return reader; |
12 |
} |
13 |
} |
14 |
public static TextWriter Out { |
15 |
get { |
16 |
if (writer == null) { |
17 |
writer = new StreamWriter(Console.OpenStandardOutput()); |
18 |
} |
19 |
return writer; |
20 |
} |
21 |
} |
22 |
public static TextWriter Error { |
23 |
get { |
24 |
if (error == null) { |
25 |
error = new StreamWriter(Console.OpenStandardError()); |
26 |
} |
27 |
return error; |
28 |
} |
29 |
} |
30…
31}
32The Console class contains three properties, In, Out, and Error, that represent the standard input, output,
33and error devices, respectively. By exposing these members as properties, the Console class can delay their
34initialization until they are actually used. For example, upon first referencing the Out property, as in
35Console.Out.WriteLine("hello, world");
36the underlying TextWriter for the output device is created. But if the application makes no reference to the
37In and Error properties, then no objects are created for those devices. end example]
38The presence of an accessor-modifier never affects member lookup (§14.3) or overload resolution (§14.4.2).
39The modifiers on the property or indexer always determine which property or indexer is bound to, regardless
40of the context of the access.
41Once a particular property or indexer has been selected, the accessibility domains of the specific accessors
42involved are used to determine if that usage is valid:
43• If the usage is as a value (§14.1.1), the get accessor shall exist and be accessible.
44• If the usage is as the target of a simple assignment (§14.13.1), the set accessor shall exist and be
45accessible.
46• If the usage is as the target of compound assignment (§14.13.2), or as the target of the ++ or --
47operators (§14.5.9, §14.6.5), both the get accessors and the set accessor shall exist and be accessible.
48[Example: In the following example, the property A.Text is hidden by the property B.Text, even in
49contexts where only the set accessor is called. In contrast, the property B.Count is not accessible to class
50M, so the accessible property A.Count is used instead.
51class A
52{
53 |
public string Text { |
54 |
get { return "hello"; } |
55 |
set { } |
56 |
} |
296