Default Values and the ClassRoom Class
-
In lab, you were asked to execute a program like this:
Note that we create a Rectangle object, but do not use the
SetLength
orSetWidth
methods to assign values to its instance variables. It displays the following output: -
This works because the instance variables
length
andwidth
have a default value of 0, even if you never assign them a value -
Local variables, like the ones we write in the
Main
method, do not have default values. You must assign them a value before using them in an expression.-
For example, this code will produce a compile error:
You cannot assume
myVar1
will be 0; it has no value at all until you use an assignment statement.
-
-
When you create (instantiate) a new object, its instance variables will be assigned specific default values based on their type:
Type Default Value Numeric types 0 string
null
objects null
bool
false
char
'\0'
-
Remember,
null
is the value of a reference-type variable that refers to “nothing” - it does not contain the location of any object at all. You cannot do anything with a reference variable containingnull
.
A class we will use for subsequent examples
-
ClassRoom: Represents a room in a building on campus
-
UML Diagram:
- There are two attributes: the name of the building (a string) and
the room number (an
int
) - Each attribute will have a “getter” and “setter” method
- There are two attributes: the name of the building (a string) and
the room number (an
-
Implementation:
- Each attribute is implemented by an instance variable with the same name
- To write the “setter” for the building attribute, we write a method
whose return type is
void
, with a singlestring
-type parameter. Its body assigns thebuilding
instance variable to the value in the parameterbuildingParam
- To write the “getter” for the building attribute, we write a method
whose return type is
string
, and whose body returns the instance variablebuilding
-
Creating an object and using its default values:
This will print the following output:
Remember that the default value of a
string
variable isnull
. When you use string interpolation onnull
, you get an empty string.
Constructors
-
Instantiation syntax requires you to write parentheses after the name of the class, like this:
-
Parentheses indicate a method call, like in
Console.ReadLine()
orenglish.GetBuilding()
-
In fact, the instantiation statement
new ClassRoom()
does call a method: the constructor -
Constructor: A special method used to create an object. It “sets up” a new instance by initializing its instance variables.
-
If you do not write a constructor in your class, C# will generate a “default” constructor for you — this is what’s getting called when we write
new ClassRoom()
here -
The default constructor initializes each instance variable to its default value — that’s where default values come from
Writing a constructor
-
Example for ClassRoom:
-
To write a constructor, write a method whose name is exactly the same as the class name
-
This method has no return type, not even
void
. It does not have areturn
statement either -
For
ClassRoom
, this means the constructor’s header starts withpublic ClassRoom
- You can think of this method as “combining” the return type and
name. The name of the method is
ClassRoom
, and its output is of typeClassRoom
, since the return value ofnew ClassRoom()
is always aClassRoom
object - You do not actually write a
return
statement, though, becausenew
will always return the new object after calling the constructor
- You can think of this method as “combining” the return type and
name. The name of the method is
-
A custom constructor usually has parameters that correspond to the instance variables: for
ClassRoom
, it has astring
parameter namedbuildingParam
, and anint
parameter namednumberParam
- Note that when we write a method with two parameters, we separate the parameters with a comma
-
The body of a constructor must assign values to all instance variables in the object
-
Usually this means assigning each parameter to its corresponding instance variable: initialize the instance variable to equal the parameter
- Very similar to calling both “setters” at once
-
Using a constructor
-
An instantiation statement will call a constructor for the class being instantiated
-
Arguments in parentheses must match the parameters of the constructor
-
Example with the
ClassRoom
constructor:This program will produce this output:
-
The instantiation statement
new ClassRoom("Allgood East", 356)
first creates a new “empty” object of typeClassRoom
, then calls the constructor to initialize it. The first argument, “Allgood East”, becomes the constructor’s first parameter (buildingParam
), and the second argument, 356, becomes the constructor’s second parameter (numberParam
). -
After executing the instantiation statement, the object referred to by
csci
has its instance variables set to these values, even though we never calledSetBuilding
orSetNumber
Methods with multiple parameters
-
The constructor we wrote is an example of a method with two parameters
-
The same syntax can be used for ordinary, non-constructor methods, if we need more than one input value
-
For example, we could write this method in the
Rectangle
class: -
The first parameter has type
int
and is named lengthFactor. The second parameter has typeint
and is namedwidthFactor
-
You can call this method by providing two arguments, separated by a comma:
The first argument, 3, will be assigned to the first parameter,
lengthFactor
. The second argument, 5, will be assigned to the second parameter,widthFactor
-
The order of the arguments matters when calling a multi-parameter method. If you write
myRect.MultiplyBoth(5, 3)
, thenlengthFactor
will be 5 andwidthFactor
will be 3. -
The type of each argument must match the type of the corresponding parameter. For example, when you call the
ClassRoom
constructor we just wrote, the first argument must be astring
and the second argument must be anint
Writing multiple constructors
-
Remember that if you do not write a constructor, C# generates a “default” one with no parameters, so you can write
new ClassRoom()
-
Once you add a constructor to your class, C# will not generate a default constructor
- This means once we write the
ClassRoom
constructor (as shown earlier), this statement will produce a compile error:ClassRoom english = new ClassRoom();
- The constructor we wrote has 2 parameters, so now you always need 2
arguments to instantiate a
ClassRoom
- This means once we write the
-
If you still want the option to create an object with no arguments (i.e.
new ClassRoom()
), you must write a constructor with no parameters -
A class can have more than one constructor, so it would look like this:
-
The “no-argument” constructor must still initialize all the instance variables, even though it has no parameters
- You can pick any “default value” you want, or use the same ones that
C# would use (0 for numeric variables,
null
for object variables, etc.)
- You can pick any “default value” you want, or use the same ones that
C# would use (0 for numeric variables,
-
When a class has multiple constructors, the instantiation statement must decide which constructor to call
-
The instantiation statement will call the constructor whose parameters match the arguments you provide
-
For example, each of these statements will call a different constructor:
The first statement calls the two-parameter constructor we wrote, since it has a
string
argument and anint
argument (in that order), and those match the parameters(string buildingParam, int numberParam)
. The second statement calls the zero-parameter constructor since it has no arguments. -
If the arguments do not match any constructor, it is still an error:
This will produce a compile error, because the instantiation statement has two arguments in the order
int
,string
, but the only constructor with two parameters needs the first parameter to be astring
.
-
Writing ToString
Methods
ToString
recap- String interpolation automatically calls the
ToString
method on each variable or value ToString
returns a string “equivalent” to the object; for example, ifnum
is anint
variable containing 42,num.ToString()
returns “42”.- C# datatypes already have a
ToString
method, but you need to write aToString
method for your own classes to use them in string interpolation
- String interpolation automatically calls the
- Writing a
ToString
method-
To add a
ToString
method to your class, you must write this header:public override string ToString()
-
The access modifier must be
public
(so other code, like string interpolation, can call it) -
The return type must be
string
(ToString must output a string) -
It must have no parameters (the string interpolation code will not know what arguments to supply)
-
The keyword
override
means your class is “overriding,” or providing its own version of, a method that is already defined elsewhere —ToString
is defined by the baseobject
type, which is why string interpolation “knows” it can callToString
on any object- If you do not use the keyword
override
, then the pre-existingToString
method (defined by the baseobject
type) will be used instead, which only returns the name of the class
- If you do not use the keyword
-
The goal of
ToString
is to return a “string representation” of the object, so the body of the method should use all of the object’s attributes and combine them into a string somehow -
Example
ToString
method forClassRoom
:- There are two instance variables,
building
andnumber
, and we use both of them - A natural way to write the name of a classroom is the building name followed by the room number, like “University Hall 124”, so we concatenate the variables in that order
- Note that we add a space between the variables
- Note that
building
is already a string, butnumber
is anint
, so string concatenation will implicitly callnumber.ToString()
—ToString
methods can call otherToString
methods - Another way to write the body would be
return $"{building} {number}";
- There are two instance variables,
-
- Using a
ToString
method-
Any time an object is used in string interpolation or concatenation, its
ToString
method will be called -
You can also call
ToString
by name using the “dot operator,” like any other method -
This code will call the
ToString
method we just wrote forClassRoom
:
-
Method Signatures and Overloading
Name uniqueness in C#
- In general, variables, methods, and classes must have unique names, but there are several exceptions
- Variables can have the same name if they are in different scopes
- Two methods can each have a local variable with the same name
- A local variable (scope limited to the method) can have the same name as an instance variable (scope includes the whole class), but this will result in shadowing
- Classes can have the same name if they are in different
namespaces
-
This is one reason C# has namespaces: you can name your classes anything you want. Otherwise, if a library (someone else’s code) used a class name, you would be prevented from using that name
-
For example, imagine you were using a “shapes library” that provided a class named
Rectangle
, but you also wanted to write your own class namedRectangle
-
The library’s code would use its own namespace, like this:
Then your own code could have a
Rectangle
class in your own namespace: -
You can use both
Rectangle
classes in the same code, as long as you specify the namespace, like this:
-
- Methods can have the same name if they have different
signatures; this is called overloading
-
We’ll explain signatures in more detail in a minute
-
Briefly, methods can have the same name if they have different parameters
-
For example, you can have two methods named Multiply in the Rectangle class, as long as one has one parameter and the other has two parameters:
C# understands that these are different methods, even though they have the same name, because their parameters are different. If you write
myRect.Multiply(2)
it can only mean the first “Multiply” method, not the second one, because there is only one argument. -
We have used overloading already when we wrote multiple constructors — constructors are methods too. For example, these two constructors have the same name, but different parameters:
-
Method signatures
- A method’s signature has 3 components: its name, the type of each parameter, and the order the parameters appear in
- Methods are unique if their signatures are unique, which is why they can have the same name
- Signature examples:
public void Multiply(int lengthFactor, int widthFactor)
— the signature isMultiply(int, int)
(name isMultiply
, parameters areint
andint
type)public void Multiply(int factor)
— signature isMultiply(int)
public void Multiply(double factor)
— signature isMultiply(double)
- These could all be in the same class since they all have different signatures
- Parameter names are not part of the signature, just their types
-
Note that the parameter names are omitted when I write down the signature
-
That means these two methods are not unique and could not be in the same class:
Both have the same signature,
SetWidth(int)
, even though the parameters have different names. You might intend the parameters to be different (i.e. represent feet vs. meters), but anyint
-type parameter is the same to C#
-
- The method’s return type is not part of the signature
- So far all the examples have the same return type (
void
), but changing it would not change the signature - The signature of
public int Multiply(int factor)
isMultiply(int)
, which is the same aspublic void Multiply(int factor)
- The signature “begins” with the name of the method; everything
“before” that does not count (i.e.
public
,int
)
- So far all the examples have the same return type (
- The order of parameters is part of the signature, as long as the types
are different
-
Since parameter name is not part of the signature, only the type can determine the order
-
These two methods have different signatures:
The signature of the first method is
Update(int, string)
. The signature of the second method isUpdate(string, int)
. -
These two methods have the same signature, and could not be in the same class:
The signature for both methods is
Multiply(int, int)
, even though we switched the order of the parameters — the name does not count, and they are bothint
type
-
- Constructors have signatures too
- The constructor
ClassRoom(string buildingParam, int numberParam)
has the signatureClassRoom(string, int)
- The constructor
ClassRoom()
has the signatureClassRoom()
- Constructors all have the same name, but they are unique if their signatures (parameters) are different
- The constructor
Calling overloaded methods
- Previously, when you used the dot operator and wrote the name of a
method, the name was enough to determine which method to execute —
myRect.GetLength()
would call theGetLength
method - When a method is overloaded, you must use the entire signature to determine which method gets executed
- A method call has a “signature” too: the name of the method, and the type and order of the arguments
- C# will execute the method whose signature matches the signature of the method call
- Example:
myRect.Multiply(4);
has the signatureMultiply(int)
, so C# will look for a method in the Rectangle class that has the signatureMultiply(int)
. This matches the methodpublic void Multiply(int factor)
- Example:
myRect.Multiply(3, 5);
has the signatureMultiply(int, int)
, so C# will look for a method with that signature in the Rectangle class. This matches the methodpublic void Multiply(int lengthFactor, int widthFactor)
- The same process happens when you instantiate a class with multiple constructors: C# calls the constructor whose signature matches the signature of the instantiation
- If no method or constructor matches the signature of the method call,
you get a compile error. You still cannot write
myRect.Multiply(1.5)
if there is no method whose signature isMultiply(double)
.
Constructors in UML
-
Now that we can write constructors, they should be part of the UML diagram of a class
- No need to include the default constructor, or one you write yourself that takes no arguments
- Non-default constructors go in the operations section (box 3) of the UML diagram
- Similar syntax to a method:
[+/-] <<constructor>> [name]([parameter name]: [parameter type])
(where <<constructor>
{=html}> is sometimes replaced with «constructor») - Note that the name will always match the class name
- No return type, ever
- Annotation “<<constructor>>” is nice, but not necessary: if the method name matches the class name, it is a constructor
-
Example for ClassRoom: