9.2. Properties#
9.2.1. Properties#
Although the content are still valid, this section will be updated later to reflect the more current syntax and discussion of properties such as in Using Properties.
A property is a member that provides a flexible mechanism to read, write, or compute the value of a data field. Properties appear as public data members, but they’re implemented as special methods called accessors.
9.2.1.1. Getters#
In instance methods you have an extra way of getting data in and out of the method: Reading or setting instance variables (fields). The simplest methods do nothing but reading or setting instance variables. We start with those:
The private in front
of the field declarations was important to keep code outside the
class from messing with the values. On the other hand we do want
others to be able to inspect the name, phone and email,
so how do we do that? Use public methods.
Since the fields are accessible anywhere inside the class’s instance methods, and public methods can be used from outside the class, we can simply code
public string GetName()
{
return name;
}
public string GetPhone()
{
return phone;
}
public string GetEmail()
{
return email;
}
These methods allow one-way communication of the name, phone and email values out from the object. These are examples of a simple category of methods: A getter simply returns the value of a part of the object’s state, without changing the object at all.
Note again that there is no static in the method heading.
The field value for the current Contact is returned.
A standard convention that we are following: Have getter methods names start with “Get”, followed by the name of the data to be returned.
In this first simple version of Contact we add one further method, to print all the contact information with labels.
public void Print()
{
Console.WriteLine (@"Name: {0}
Phone: {1}
Email: {2}", name, phone, email);
}
Again, we use the instance variable names, plugging them into a format string.
Remember the @ syntax
for multiline strings gives us verbatim string literals that are interpreted literally
without escape sequences.
You can see and see the entire Contact class in contact1/contact1.cs.
This is your first complete class defining a new type of object. Look carefully to get used to the features introduced, before we add more ideas:
9.2.1.2. This Object#
We will be making an elaboration on the Contact class from here on. We introduce new parts individually, but the whole code is in contact2/contact2.cs.
The current object is implicit inside a constructor or instance method definition,
but it can be referred to explicitly. It is called this.
In a constructor or instance method, this is automatically a legal
local variable to reference.
You usually do not need to
use it explicitly, but you could. For example the current Contact object’s
name field could be referred to as either this.name or the shorter plain name.
In our next version of the Contact class we will see several places where
an explicit this is useful.
In the first version of the constructor, repeated here,
public Contact(string fullName, string phoneNumber, string emailAddress)
{
name = fullName;
phone = phoneNumber;
email = emailAddress;
}
we used different names for the instance variables and the formal parameter names that we used to initialize the instance variables. We chose reasonable names, but we are adding extra names that we are not going to use later, and it can be confusing. The most obvious names for the formal parameters that will initialize the instance variables are the same names.
If we are not careful, there is a problem with that. An instance variable, however named, and a local variable are not the same. This is nonsensical:
public Contact(string name, string phone, string email)
{
name = name; // ????
...
Logically we want this pseudo-code in the constructor:
instance variable
name=local variablename
We have to disambiguate the two uses. The compiler always looks for
local variable identifiers first, so plain name will refer to the local
variable name declared in the formal parameter list.
This local variable identifier hides the matching instance variable
identifier. We have to do something else to refer to the instance variable.
The explicit this object comes to the rescue: this.name refers to
a part of this object. It must refer to the
instance variable, not the local variable. Our alternate constructor is:
public Contact(string name, string phone, string email)
{
this.name = name;
this.phone = phone;
this.email = email;
}
9.2.1.3. Setters#
The original version of Contact makes a Contact object be immutable: Once it is created with the constructor, there is no way to change its internal state. The only assignments to the private instance variables are the ones in the constructor. In real life people can change their email address. We might like to allow that with our Contact objects. Users can read the data in a Contact with the getter methods. Now we need setter methods. The naming conventions are similar: start with “Set”. In this case we must supply the new data, so setter methods need a parameter:
public void SetPhone(string newPhone)
{
phone = newPhone;
}
In SetPhone, like in our original constructor, we illustrate using a
new name for the parameter that sets the instance variable. For
comparison we use the alternate identifier matching approach in the
other setter:
public void SetEmail(string email)
{
this.email = email;
}
Now we can alter the contents of a Contact:
Contact c1 = new Contact("Marie Ortiz", "773-508-7890", "mortiz2@luc.edu");
c1.SetEmail ("maria.ortiz@gmail.com");
c1.SetPhone("555-555-5555");
c1.Print();
would print
Name: Marie Ortiz
Phone: 555-555-5555
Email: maria.ortiz@gmail.com
9.2.2. ToString Override#
All object in C# have a ToString method because all types inherit from the base class
System.Object. It converts an object to its string representation for display. It is used
to perform internal string concatenation and Write.
9.2.2.1. Default ToString behavior#
By default, ToString returns returns the fully qualified name of the type of the Object. For example:
namespace IntroCSCS
{
class Animal
{
public string name;
public Animal(string name)
{
this.name = name; }
}
}
public class TestAnimal
{
public static void Main()
{
Animal frog = new Animal("Froggy");
Console.WriteLine(frog.ToString()); ///// print: IntroCSCS.Animal; Animal is the type
Console.WriteLine(frog.name); ///// print: Froggy
}
}
}
9.2.2.2. Overriding ToString#
Think more generally about string representations:
All the built-in type values can be concatenated into strings with the '+' operator,
or displayed with `Console.Write`. We would like that behavior with our custom types,
too.
Types frequently `override` the Object.ToString method to provide a more suitable string
representation or display of a particular type instead of the default fully qualified type name.
For example:
class Employee
{
public string Name { get; set; }
public int Salary { get; set; }
public override string ToString()
{
return "Employee: " + Name + " " + Salary; ///// returns object data
}
}
class Test
{
public static void Main()
{
Employee employee = new Employee { Name = "John", Salary = 100000 };
Console.WriteLine(employee.ToString()); ///// print: Employee: John 100000
Console.WriteLine(employee); ///// print: Employee: John 100000
///// as ToString is the default output
}
}
See what the ToString method does now: it uses the object state (e.g., the values of variables name and salary in this case) and implicitly covert and return a single string representation of the object.
9.2.2.3. Print() Customization#
You can further customize the ToString overriding. For example, in the preceding Employee class,
in addition to the following ToString override, you might like to have a convenience method
Print using ToString. We want one instance method, Print to call another instance
method ToString for the same object:
public void Print()
{
Console.WriteLine(ToString());
// Console.WriteLine(this); ///// "this" returns the same as ToString()
}
Now take a look at the test and output:
Employee employee = new Employee { Name = "John", Salary = 100000 };
Console.WriteLine(employee.ToString());
Console.WriteLine(employee);
employee.Print();
tcn85@mac:~/workspace/introcscs/Ch10ClassesLab$ dotnet run
Employee: John 100000
Employee: John 100000
Employee: John 100000
{rubric} footnote
[1] See the answer at What is the difference between a field and a property?
[2] Accessibility Levels (C# Reference).
[3] See internal
Footnotes