property
An attribute is a member that provides flexible mechanisms to read, write, or calculate the values of private fields. Attributes can be used as public data members, but they are actually special methods called "accessors". This makes it easy to access data and also helps improve the security and flexibility of the method.
In this example, the TimePeriod class stores the time period. This class stores time internally in seconds, but a property named Hours allows clients to specify time in hours. The accessor of the Hours property performs conversion between hours and seconds.
class TimePeriod { private double seconds; public double Hours { get { return seconds / 3600; } set { seconds = value * 3600; } } } class Program { static void Main() { TimePeriod t = new TimePeriod(); // Assigning the Hours property causes the 'set' accessor to be called. = 24; // Evaluating the Hours property causes the 'get' accessor to be called. ("Time in hours: " + ); } }
Output:
Time in hours: 24
Expression body definition
It is common to directly return properties that only expression results. The following syntax shortcut uses => to define these properties:
public string Name => First + " " + Last;
The property must be read-only and you cannot use the get accessor keyword.
Use Properties
Attributes combine multiple aspects of fields and methods. For users of an object, the property is displayed as a field and accessing the property requires the same syntax. For class implementers, properties are one or two code blocks representing a get accessor and/or a set accessor. When a property is read, the code block of the get accessor is executed; when a new value is assigned to the property, the code block of the set accessor is executed. Properties that do not have set accessors are considered read-only properties. Properties that do not have a get accessor are considered write-only properties. The attributes that have both accessors are read and write attributes.
Attributes have multiple uses: they can verify data before allowing changes; they can transparently expose data on a class whose data is actually retrieved from other sources, such as databases; when the data is changed, they can take actions, such as raising an event or changing the value of other fields.
Properties are declared in class blocks: specify the access level of the field, then specify the type and name of the property, and then follow the code block that declares the get accessor and/or set accessor. For example:
public class Date { private int month = 7; // Backing store public int Month { get { return month; } set { if ((value > 0) && (value < 13)) { month = value; } } } }
In this example, Month is declared as an attribute so that the set accessor ensures that the Month value is set between 1 and 12. The Month property uses a private field to track the actual value. The real location of the data of an attribute is often called the "backup storage" of the attribute. It is common for properties to use private fields as backing storage. Marking a field as private ensures that the field can only be changed by calling the property.
。
get accessor
The get accessor body is similar to the method body. It must return the value of the attribute type. Execution of the get accessor is equivalent to reading the value of the field. For example, when a private variable is being returned from the get accessor and optimization is enabled, the call to the get accessor method is inlined by the compiler, so there is no overhead of method calls. However, since the compiler does not know which method is actually called at runtime, the virtual get accessor cannot be inlined. Here is the get accessor that returns the value of the private field name:
class Person { private string name; // the name field public string Name // the Name property { get { return name; } } }
When referring to a property, the get accessor is called to read the value of the property unless the property is an assignment target. For example:
Person person = new Person(); //... (); // the get accessor is invoked here
The get accessor must be terminated with a return or throw statement, and control cannot leave the accessor body.
Changing the state of an object by using the get accessor is not a good programming style. For example, the following accessors have side effects of changing the state of the object each time they access the number field.
private int number; public int Number { get { return number++; // Don't do this } }
The get accessor can be used to return field values, or to calculate and return field values. For example:
class Employee { private string name; public string Name { get { return name != null ? name : "NA"; } } }
In the previous code segment, if the Name property is not assigned, it will return the value NA.
set accessor
The set accessor is similar to a method with a return type void. It uses an implicit parameter called value, whose type is the type of the property. In the following example, add the set accessor to the Name property:
class Person { private string name; // the name field public string Name // the Name property { get { return name; } set { name = value; } } }
When assigning values to attributes, the set accessor is called with the parameter that provides the new value. For example:
Person person = new Person(); = "Joe"; // the set accessor is invoked here (); // the get accessor is invoked here
In the set accessor, it is wrong to use the implicit parameter name value for local variable declarations.
This example illustrates instance, static, and read-only properties. It accepts the employee's name from the keyboard, increments NumberOfEmployees by 1, and displays the employee's name and number.
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() { = 107; Employee e1 = new Employee(); = "Claude Vige"; ("Employee number: {0}", ); ("Employee name: {0}", ); } }
Output:
Employee number: 108 Employee name: Claude Vige
This example shows how to access properties in a base class that are hidden by another property with the same name in the derived class.
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. = "John"; // Base class property. ((Employee)m1).Name = "Mary"; ("Name in the derived class is: {0}", ); ("Name in the base class is: {0}", ((Employee)m1).Name); } }
Output:
Name in the derived class is: John, Manager Name in the base class is: Mary
Here are the key points from the previous example:
Attribute Name in derived class Hides attribute Name in base class. In this case, the new modifier is used in the attribute declaration of the derived class:
public new string Name
Transform (Employee) is used to access hidden properties in the base class:
((Employee)m1).Name = "Mary";
In this example, Cube and Square implement the abstract class Shape and override its abstract Area property. Pay attention to the use of the override modifier on the attribute. The program accepts the input side length and calculates the area of the square and cube. It also takes the input area and calculates the corresponding side lengths of the square and cube.
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 = (value); } } } class Cube : Shape { public double side; public Cube(double s) { side = s; } public override double Area { get { return 6 * side * side; } set { side = (value / 6); } } } class TestShapes { static void Main() { // Input the side: ("Enter the side: "); double side = (()); // Compute the areas: Square s = new Square(side); Cube c = new Cube(side); // Display the results: ("Area of the square = {0:F2}", ); ("Area of the cube = {0:F2}", ); (); // Input the area: ("Enter the area: "); double area = (()); // Compute the sides: = area; = area; // Display the results: ("Side of the square = {0:F2}", ); ("Side of the cube = {0:F2}", ); } }
Output:
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