You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
613 lines
13 KiB
Markdown
613 lines
13 KiB
Markdown
---
|
|
marp: true
|
|
paginate: true
|
|
math: mathjax
|
|
theme: buutti
|
|
title: 7. Classes And Objects
|
|
---
|
|
|
|
# Classes And Objects
|
|
|
|
<!-- headingDivider: 5 -->
|
|
<!-- class: invert -->
|
|
|
|
## Overview
|
|
|
|
* Namespaces
|
|
* Classes
|
|
* Object Variables
|
|
* Access Specifiers
|
|
* Class Members
|
|
* Constructors
|
|
* Value & Reference Types
|
|
|
|
## Namespaces
|
|
|
|
* Using namespaces helps you to organize the scope of your project, so that...
|
|
* Variable names do not clash
|
|
* Related variables are well-contained in their own namespaces
|
|
* $\Rightarrow$ easy to import what you need
|
|
* By default, when creating a new project and adding classes in Visual Studio, everything is contained within a namespace named after your project name
|
|
|
|
---
|
|
|
|
* If you need a class, interface, etc to be accessible from anywhere, create it within that namespace and set it as `public`
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
// Note that namespaces can have
|
|
// subdirectories, as Models here
|
|
|
|
namespace MyAwesomeApp.Models
|
|
{
|
|
public class Student
|
|
{
|
|
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
// This is how you would use the
|
|
// Student class in other code
|
|
|
|
using MyAwesomeApp.Models
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
* Wait, what's a class, though?
|
|
|
|
|
|
|
|
## Classes
|
|
|
|
* [Classes](https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/language-specification/classes) in C# are blueprints for specific kinds of collections of data & functionality
|
|
* ***Instance*** of a class is an implementation of that class, also referred to as an ***object***
|
|
* As C# is an object-oriented language, almost everything has to happen in a class
|
|
* Thus, we have already used a class named `Program` when creating a new console application:
|
|
```csharp
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
|
|
}
|
|
}
|
|
```
|
|
|
|
### Why classes?
|
|
|
|
* Classes are a core feature of the ***object-oriented programming*** paradigm popularized by Java in the 90s
|
|
* (See [Lecture 8](8-inheritance-and-abstract-classes#extra-object-oriented-programming))
|
|
* Classes provide *__encapsulation__*: Connected concepts and functionalities are neatly contained within a class
|
|
* This will ***ideally*** result in shorter files, less repetitive code and ***hopefully*** overall better readability
|
|
* No paradigm is idiot-proof, though...
|
|
* Shorter files can also mean that it's hard to find the actual functionality from hundreds of short files full of intermediate classes and class factories
|
|
|
|
### Creating a class
|
|
|
|
* You can create a new class by writing
|
|
```csharp
|
|
class Student
|
|
{
|
|
|
|
}
|
|
```
|
|
* Create variables inside a class just like before inside the `Program` class:
|
|
```csharp
|
|
class Student
|
|
{
|
|
int id;
|
|
string name;
|
|
}
|
|
```
|
|
* These variables declared directly inside classes are called *__fields__*.
|
|
|
|
### Instancing a class
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
* New instances of a class can be created with the `new` keyword
|
|
* All instances of a class have exactly the same fields and methods, but they can have different values
|
|
* In the following example a few ***objects*** of class `Student` are created
|
|
* However, these objects have no functionality yet
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
|
|
namespace MyAwesomeApp
|
|
{
|
|
class Student
|
|
{
|
|
|
|
}
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
Student student1 = new Student();
|
|
Student student2 = new Student();
|
|
Student student3 = new Student();
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
### Object variables
|
|
|
|
* Variables inside of an object can be accessed with the `.` operator
|
|
* However, the following syntax results in an error:
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
Student class:
|
|
|
|
```csharp
|
|
class Student
|
|
{
|
|
int id;
|
|
}
|
|
```
|
|
|
|
Error:
|
|

|
|
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
Program class:
|
|
|
|
```csharp
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
Student student = new Student();
|
|
student.id = 12345678;
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
---
|
|
|
|
Let's fix the previous example by changing the *__access specifier__* of the variable `id` to `public`:
|
|
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
Student class:
|
|
|
|
```csharp
|
|
class Student
|
|
{
|
|
public int id;
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
Student student = new Student();
|
|
student.id = 12345678;
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
* The value of the variable `id` of the object `student` is now `12345678`
|
|
|
|
## Access Specifiers
|
|
|
|
* *__Access specifiers__* can be used to get additional level of protection inside classes
|
|
* Variables specified with `private` are accessible only inside the containing class
|
|
* This is the ***default*** access!
|
|
* Variables specified with `public` are accessible outside of the class
|
|
```csharp
|
|
class Student
|
|
{
|
|
int id; // Accessible only inside the class
|
|
private string name; // Accessible only inside the class
|
|
public string address; // Accessible everywhere within the namespace
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
* Continuing on the class in the previous example: if we follow the `student` variable with a dot, Visual Studio IntelliSense will only suggest the `address` variable, because it was the only `public` variable of the `Student` class!
|
|
|
|

|
|
|
|
## Classes: An example
|
|
|
|
<div class='columns12' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Student
|
|
{
|
|
public int id;
|
|
public string name;
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
Student student1 = new Student();
|
|
Student student2 = new Student();
|
|
|
|
student1.id = 22225555;
|
|
student1.name = "Johannes Kantola";
|
|
student2.id = 44441111;
|
|
student2.name = "Rene Orosz";
|
|
|
|
Console.WriteLine(student1.name);
|
|
// Outputs Johannes Kantola
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
## Exercise 1
|
|
<!--_class: "exercise invert" -->
|
|
|
|
1) Create a console application that has a class `User` that contains two fields: `string userName` and `string password`
|
|
2) Create a main loop where the user is asked repeatedly for a new username and password. The values are stored in a new `User` instance and that instance is saved to a list of users
|
|
3) Print all stored usernames every time a new user is created
|
|
|
|
## Class methods
|
|
|
|
* As mentioned in [Lecture 6](6-methods), we can create methods inside of our classes:
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Person
|
|
{
|
|
public string firstName;
|
|
public string lastName;
|
|
public string FullName()
|
|
{
|
|
return firstName + " " + lastName;
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
Person someDude = new Person();
|
|
someDude.firstName = "Johannes";
|
|
someDude.lastName = "Kantola";
|
|
Console.WriteLine(someDude.FullName());
|
|
// Outputs Johannes Kantola
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
* If the method is public, it can be called from outside of the class
|
|
|
|
## Constructors
|
|
|
|
* Constructors are class methods which are called once the object is initialized
|
|
* Constructors are created by creating a class method with the same name as the class:
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class User
|
|
{
|
|
public User()
|
|
{
|
|
Console.WriteLine
|
|
("New user created!");
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
User user = new User();
|
|
// Outputs "New user created!"
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
* ***Note:*** In Visual Studio, just write `ctor` and press tab twice to quickly create a constructor!
|
|
|
|
## Constructors with parameters
|
|
|
|
* You can pass in parameters to the constructor at initialization:
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Car
|
|
{
|
|
private string model;
|
|
private int year;
|
|
private int doors;
|
|
|
|
public Car(string modelName, int modelYear)
|
|
{
|
|
model = modelName;
|
|
year = modelYear;
|
|
doors = 5;
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
Car lada = new Car("Niva", 1984);
|
|
Car ford = new Car("Focus", 2010);
|
|
// Both cars have 5 doors by default
|
|
}
|
|
}
|
|
```
|
|
</div>
|
|
</div>
|
|
|
|
## Exercise 2
|
|
<!--_class: "exercise invert" -->
|
|
|
|
1) Create a console application with a class Animal, that contains
|
|
* two strings, `name` and `sound`
|
|
* a method `Greet()` that prints `<name> says <sound>!` (without the angled brackets)
|
|
2) Create a few animals with different names and sounds
|
|
3) Call their greet methods from the main program
|
|
|
|
## Properties
|
|
|
|
* When working with C#, you will eventually see [properties](https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/properties) being used at some point
|
|
* They are fields with defined `get` and `set` ***accessors***
|
|
* These accessors are often referred to as a ***getter*** and a ***setter***
|
|
* Properties allow the manipulation of private fields from outside of the class:
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class User
|
|
{
|
|
private int id;
|
|
public int Id
|
|
{
|
|
get { return id; }
|
|
set { id = value; }
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
User admin = new User();
|
|
admin.Id = 12345678;
|
|
Console.WriteLine(admin.Id);
|
|
// This outputs 12345678
|
|
}
|
|
}
|
|
```
|
|
|
|
### Auto Properties
|
|
|
|
* Auto properties are a shorthand version of the same:
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class User
|
|
{
|
|
public int Id { get; set; }
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
User admin = new User();
|
|
admin.Id = 12345678;
|
|
Console.WriteLine(admin.Id);
|
|
// This outputs 12345678
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
</div>
|
|
|
|
* ***Note:*** In Visual Studio, just write `prop` and press tab twice to quickly create an auto property
|
|
|
|
### Why properties?
|
|
|
|
* Why use properties if we could just use public fields?
|
|
* We don't always want to expose the exact same variable that is used inside the class to outside
|
|
* Properties allow class fields to be *__read-only__* or *__write-only__*:
|
|
```csharp
|
|
public int Id { get; } // This field is read-only
|
|
public int Password { private get; set; } // This field is write-only
|
|
```
|
|
* Also, we can add whatever functionality we want to getters and setters, which makes properties a very flexible feature
|
|
* Don't know when a property is being changed? Just print its value to the console in the setter.
|
|
* Properties are supported in *__interfaces__*, while fields are not
|
|
* We will cover interfaces later in [Lecture 9](9-interfaces#interfaces)
|
|
|
|
## Value and Reference Types
|
|
|
|
* See the example below. If this program is executed, what will be the output?
|
|
* `int`, `double`, `char` and other primitive data types are of the *__value type__*
|
|
* The variable stores the *__actual data__* in memory
|
|
* Every copy of the variable will be stored in a separate location
|
|
|
|
<div class='columns21' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Program{
|
|
static void Main(string[] args)
|
|
{
|
|
int user = 1;
|
|
int otherUser = user;
|
|
user = 2;
|
|
Console.WriteLine("Value of otherUser: " + otherUser);
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|

|
|
|
|
</div>
|
|
</div>
|
|
|
|
---
|
|
|
|
* Another example: When this program is executed, what will be the output?
|
|
* `string`, `Array` and Classes are of the *__reference type__
|
|
* The variable stores ***the address of the data*** being stored in memory
|
|
* Every copy of the variable points to the same ***address***
|
|
|
|
<div class='columns' markdown='1'>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class Program
|
|
{
|
|
static void Main(string[] args)
|
|
{
|
|
User user = new User();
|
|
user.id = 1;
|
|
User otherUser = user;
|
|
user.id = 2;
|
|
Console.WriteLine(
|
|
"Value of otherUser.id: " + otherUser.id);
|
|
}
|
|
}
|
|
```
|
|
|
|
</div>
|
|
<div markdown='1'>
|
|
|
|
```csharp
|
|
class User
|
|
{
|
|
public int id;
|
|
}
|
|
```
|
|
|
|

|
|
|
|
</div>
|
|
</div>
|
|
|
|
---
|
|
|
|

|
|
|
|
<!-- Location in memory
|
|
|
|
int userId = 42;
|
|
|
|
int otherUserId = userId;
|
|
|
|
| 42 |
|
|
| :-: |
|
|
| 42 |
|
|
|
|
Changing the value in the object will affect all variables referencing it!
|
|
|
|
| points to |
|
|
| :-: |
|
|
| points to |
|
|
|
|
User user = new User(42);
|
|
|
|
User otherUser = user;
|
|
|
|
| user { id = 42 } |
|
|
| :-: | -->
|
|
|
|
## Exercise 3
|
|
<!--_class: "exercise invert" -->
|
|
|
|
1) Classes can be stored in a separate file. To quickly create a new class in Visual Studio: right mouse click your project name (not the solution!) in Solution Explorer *-> Add -> Class...*
|
|
* Name your class `TestClass`.
|
|
2) Back in your main method, create an instance of `TestClass`. The compiler should not give any errors.
|
|
3) Now rename the namespace that your `TestClass` is in to `TestNamespace`. Go back to your main method. The `TestClass` instantiation should not work anymore. Try to fix the issue without going back to `TestClass`!
|
|
|
|
<!-- ## Exercise 4
|
|
|
|
Modify previous solution by using methods and class. -->
|
|
|