Learning to Code — Part 8b: More on Classes

Scott Rosenbloom
17 min readAug 2, 2018

--

THIS

Next up, the SoloLearn app goes through the this keyword, which refers to the current instance of the class, or, the current object. One important thing that they point out is using it to distinguish class members from other data like local variables of a method.

Here’s an example:

class Person {
private string name;
public Person(string name) {
this.name = name;
}
}

In the code above, this is described within the app this way:

Here, in this.name, this represents the member of the class, whereas name represents the parameter of the constructor.

This didn’t completely make sense to me, until I read one of the comments by someone named Zoidberg:

You can use the keyword “this” to specify about what object you are talking. It always refers to the current instance of a class (object). For example an object that you just(!) created…

In the above example the “this” keyword is used in the constructor of the class Person. If you were to instatiate an object of the class Person in the Main function it would look like this:

Person p1 = new Person(“John”);

In this case the line “this.name = name” is equivalent to “p1.name = name”, as p1 is the current object. The variable name (string) on the right side of the equals sign has the value “John”. The one on the left side refers to the property of the class Person.

This made more sense to me, as I interpreted it this way:

  • When I create a new instance of the Person class, and call it p1, I’m sending it the name John.
  • Within the Person class, a private variable (meaning it’s only accessible within the class), which is of the data type string, is created, and is called name.
  • public Person(string name) { this.name = name; } is the constructor. Where it says this.name, it’s like saying, the current instatiation of the class Person, which is called p1. Since, when the Person class was sent the value “John”, and the constructor takes it and assigns it to the string-based variable called name, this.name = name; is the equivalent of saying, p1.name = name (and in the name property of p1 is equal to John).

The app also mentions that this can also be used for passing the current instance to a method as a parameter, like the following:

ShowPersonInfo(this);

They don’t go into any explanation of what this means, so, returning to Dot Net Perls page about this, I begin to understand that what this can do is send values from one instantiated object, that may have been determined or calculated in some way by a member of the class that was called, to another member method within that class or another class.

class Net {
public string Name { get; set; }
public Net (Perl perl) {
this.Name = perl.Name;
}
}
class Perl {
public string Name { get; set; }
public Perl(string name) {
this.Name = name;
Net net = new Net (this);
Console.WriteLine(net.Name);
}
}
class Program {
static void Main() {
Perl perl = new Perl(“Sam”):
}

I think I understand what’s happening here, but I’m going to walk through it anyway.

This time, however, I’m going to start from the Main method:

  • Within the Program class, inside the Main method, a new instance of the class called Perl is instantiated, and will be called perl. Also during that process, when the Perl class is called, it is sent the string-based argument (value) Sam.
  • Within the Perl class, first a publicly accessible, string-based variable called Name is created, gets the value Sam that was sent to the class when the object (called perl) was instantiated, and sets it as the value (of the variable Name).
  • Next, the publicly accessible constructor method called Perl is executed and, at the same time, declares a string-based variable called name.
  • The next line, this.Name = name; is the equivalent of perl.Name = name, or perl.Name = Sam. In other words, the current value of the property Name (of this instance of the Perl object, called perl) is now Sam.
  • After that, a new instance of the Net class is instantiated, and is called net. Also during that process, when the Net class is called, it is sent this, which are whatever the current values of the properties (and there’s only 1, called name, which is Sam).
  • Within the Net class, first a publicly accessible string-based variable called Name is created, gets the value Sam that was sent to the class when the object (called net) was instantiated, and sets it as the value (of the variable Name).
  • Next, the publicly accessible constructor method called Net is executed and, at the same time, declares a variable called perl of type Perl.
  • The next line, this.Name = perl.Name; is the equivalent of net.Name = perl.Name; The property called Name of the instance called perl was sent earlier. It’s value is Sam. So, net.Name = Sam. In other words, the current value of the property Name (of the instance of the Net object, called net) is now Sam.
  • Returning to the class Perl, the next line says to print to the screen, the current value of the Name property of the instance net (which is an instance of the class called Perl) which is Sam. In other words, Sam is printed to the screen.

That was a bit of a journey, but I definitely understand now how the information travels from one place to another, to another, and then back again.

readonly

The second half of this section talks about the readonly modifier. According to the app

…it prevents a member of the class from being modified after construction. It means that the field declared as readonly can be modified only when you declare it or from within the constructor.

If there is a modification attempt on the name field in the code below, other than within the class within which it’s declared, it will generate an error:

class Person {
private readonly string name = “John”;
public Person(string name) {
this.name = name;
}
}

While the readonly is similar to const field, the difference is that the const field must be initialized when it’s declared, whereas the readonly field can be declared without initialization. So while the following is OK:

readonly string name;

The following is not OK:

const double PI;

To make it OK, it would need to be:

const double PI = 3.14;

The next difference, is that a readonly field can be changed within the constructor, a const field cannot. Finally, and I think this one is pretty powerful, a readonly field can be assigned a value as the result of a calculation, while a const cannot. So, while the following is OK:

readonly double a = Math.Sin(60);

The following is not OK:

const double b = Math.Sin(60);

INDEXERS

OK, 2 topics left. First indexers, which allow objects to be indexed like an array. The SoloLearn app relates this back to what they mentioned about a string variable actually being an object of the String class, and that it’s actually an array of char (or character) objects. This is actually how the string class implements an indexer so we can access any character (or Char object) by it’s index.

string str = “Hello World”;
char x = str[4];
Console.WriteLine(x);

When executed, the code above prints the letter “o” to the screen as it is in the 4th indexed position. One difference they mention between arrays and indexers, is that while arrays use integer indexes, indexers can use any type of index, such as strings, characters, etc. I needed a bit more information on this, and got it in the comments. Basically, with an array, you can only use integers “between the brackets”. With indexers, you can use anything.

That didn’t completely clear it up so I Googled it and found that, while there were a number of posts about it, it was still somewhat confusing to me. I watched a few videos on YouTube, and it wasn’t until I watch one called C# — Indexer by tutorials point (india) pvt. ltd., did I get “slightly” closer to understanding.

Indexers allow you to use the relationship between multiple set of values, to retrieve values. Here are my notes:

  • First, he creates a class called Employee, that has 3 properties called Id, EmpName and Salary, whose values are assigned using get and set.
  • Then, he creates another class called Department, with 2 properties called DeptId and DeptName, whose values are also assigned using get and set. He also creates an array in the Department class called EmpList of the data type Employee (which is that first class he created). He does that because there is a “relationship” between the Department and the Employee.
  • Still within the Department class, he creates a constructor with some hardcoded values for DeptId and DeptName, and then, the previously initialized array called EmpList (of the data type Employee) gets declared, with 3 spots for new “Employees”, within which there are values of Id, EmpName and Salary (which were established within the Employee class).
  • Then, he creates 2 methods to be able to get an employee by either their id or their name, based on what a user enters when searching through the department.

After that, he mentions that instead of creating these two methods, he can create an indexer instead, with only a get accessor, to take the users input. It will take the entered id or name, search through the entire (applicable) array for a match, and then return the employee.

Finally, in the Main method, he instantiates a new object called dept of the Department class. Within it, he searches within the object for the name of an employee using an id. He also does a search within the object for the id of an employee using a name.

I definitely understand this better than before, but would like to enter it into Visual Studio to see it in action. Here it is:

namespace Indexer {
class Employee {
public int Id { get; set; }
public string EmpName { get; set; }
public double Salary { get; set; }
}
class Department {
public int DeptId { get; set; }
public string DeptName { get; set; }
Employee[] EmpList;

public Department() {
DeptId = 10;
DeptName = “Sales”;
EmpList = new Employee[3] {
new Employee { Id = 101, EmpName = “Alex”, Salary = 50000},
new Employee { Id = 102, EmpName = “Brad”, Salary = 45000 },
new Employee { Id = 103, EmpName = “Chris”, Salary = 40000 }
};
}
public Employee this[int id] {
get {
foreach (Employee emp in EmpList) {
if (id == emp.Id)
return emp;
}
return null;
}
}
public Employee this[string name] {
get {
foreach (Employee emp in EmpList) {
if (name == emp.EmpName)
return emp;
}
return null;
}
}
}
class Program {
static void Main(string[] args) {
Department dept = new Department();
Console.WriteLine(dept[101].EmpName);
Console.WriteLine(dept[“Brad”].Id);
Console.ReadLine();
}
}
}

And now, here’s my explanation of what’s happening, starting with the Main class, when the program is instantiated:

  • A new object called dept is instantiated of the Department class, and then, printed to the screen, is the result of the line dept[101].EmpName.
  • Within the Department class, an integer-based property called DeptId is declared which can get a value from the user’s input, and then will set it as the current value of DeptId.
  • Next, a string-based property called DeptName is declared, which can get a value from the user’s input, and will then set it as the current value of DeptName.
  • After that, an array is declared of type Employee, called EmpList.
  • Still within the Department class, we see the Department method where “hardcoded” values are first assigned to the properties. First, DeptId is assigned the value of 10, and DeptName is assigned the value Sales.
  • Next, a new array object called EmpList is declared, of type Employee (calling the Employee class), and it is configured to have 3 elements within it.
  • On the next three lines, 3 new objects of the Employee class are instantiated and, when this is done, values for the properties within each are assigned. When the first Employee class is instantiated, the properties within them get these values, and set each to be the values of the indicated properties (i.e. Id, EmpName and Salary). The 3 bullet points above essentially create the “database” of employees.
  • Back to printing the results of dept[101].EmpName, we look at the first indexer called Employee, as it is the only place within the Department class that deals directly with the instantiated object (which we can tell by the use of the keyword this), and also receives an integer-based argument. This indexer, called Employee, looks at this instance of the object, and takes from the calling of it, the value within the brackets, and assigns it to the integer-based variable called id.
  • Next, it will then get the results of a foreach loop. The first time through, for the first Employee element within the array called EmpList, assign it’s properties to the variable called emp.
  • It will then test whether the current value of id, which was sent to it by the user as “101”, is equal to, within the current emp object (or, the first one within the array), the value of Id. In other words, does 101 equal, from within the first element in the EmpList array, the value assigned for Id, which is 101? Or, does 101 equal 101?
  • Since it does, it will return the current value of emp, which is the first element within the EmpList array, back to where it was initially called. In other words, you can swap out dept[101] for the current value of emp, which is the first element within the EmpList array.
  • The .EmpName looks to take the value assigned to EmpName from that array element. In other words, what is the value of EmpName within the array element that was sent back? The value is Alex. Therefore, Alex is printed to the screen.

The next line within the Main class wants to go through the same process but with the value Brad, retrieving from the resulting array element, the value of it’s Id.

  • This time, we look at the second indexer called Employee, as it is the only place within the Department class that deals directly with the instantiated object (which we can tell by the use of the keyword this), and also receives an string-based argument. This indexer, called Employee, looks at this instance of the object, and takes from the calling of it, the value within the brackets, and assigns it to the string-based variable called name.
  • Next, it will then get the results of a foreach loop. The first time through, for the first Employee element within the array called EmpList, assign it’s properties to the variable called emp.
  • It will then test whether the current value of name, which was sent to it by the user as “Brad”, is equal to, within the current emp object (or, the first one within the array), the value of EmpName. In other words, does Brad equal, from within the first element in the EmpList array, the value assigned for EmpName, which is Alex? Or, does Brad equal Alex?
  • Since is does not, the foreach loop moves onto the next item. It tests whether the current value of name, which was sent to it by the user as “Brad”, is equal to, within the current emp object (or, the second one within the array), the value of EmpName. In other words, does Brad equal, from within the second element in the EmpList array, the value assigned for EmpName, which is Brad? Or, does Brad equal Brad?
  • Since it does, it will return the current value of emp, which is the second element within the EmpList array, back to where it was initially called. In other words, you can swap out dept[“Brad”] for the current value of emp, which is the second element within the EmpList array.
  • The .Id looks to take the value assigned to Id from that array element. In other words, what is the value of Id within the array element that was sent back? The value is 102. Therefore, 102 is printed to the screen.

Once again, this was pretty intense, but the point is, with an indexer, you can search and retrieve values based on any type from within an array, as opposed to with just an array, where you can retrieve values only based on integers.

Moving on (and it’s possible that this next part will explain all the stuff I just did), the app talks about how declaring an indexer is similar to a property. The difference is that indexer accessors require an index. As we saw above, the accessors used for defining an indexer are get and set. The other difference is, as a result of this, where properties return or set a specific data member, indexers return or set a particular value from the object instance (using the this keyword as we saw above in the two indexers).

The code example they give is a bit more generic that what we saw above (and may have even made this easier to understand):

class Clients {
private string[] name = new string[10];
public string this[int index] {
get {
return names[index];
}
set {
names[index] = value
}
}
}
Clients c = new Clients();
c[0] = “Dave”;
c[1] = “Bob”;
Console.WriteLine(c[1]);

Here’s what happens:

  • A new object called c is instantiated of the Clients class.
  • Within the Clients class, first, a new array object called string is instantiated of the built-in class called string, and configured with 10 elements.
  • An indexer called string is declared and assigns whatever value is set to it, as the value of the integer-based variable called index.
  • First, 0 is set as the value of index and, looking back to where it was called from, as Dave is the value for c[0], it returns it as the value of names[index], to be used in the future.
  • Then, it sets the current value of names[index], which is Dave, as the value of the first spot of the array.
  • Next, 1 is set as the value of index and, looking back to where it was called from, as Bob is the value for c[1], it returns it as the value of names[index], to be used in the future.
  • Then, it sets the current value of names[index], which is Bob, as the value of the second spot of the array.
  • As there are no more values being sent to the array, we move to the next line of code, which prints the value of the element in index position 2 of the array object c, which is Bob, to the screen.

One last thing it mentions, which I think is exceedingly important, is that…

…you typically use an indexer if the class represents a list, collection, or array of objects.

Their explanation was definitely more (quickly) to the point, but I think understanding the more complex example from the video, has made this simple example much easier to follow.

OPERATOR OVERLOADING

And finally, operator overloading refers to the ability to redefine operators with custom actions. The example code they give refers to the result of adding two boxes, each with its own height and width, together to get a single, larger box. Overall, they’re adding the widths of each, and the heights of each, to get a new width and height, for a third box.

class Box {
public int Height { get; set; }
public int Width { get; set; }
public Box(int h, int w) {
Height = h;
Width = w;
}
public static Box operator+(Box a, Box b) {
int h = a.Height + b.Height;
int w = a.Width + b.Width;
Box res = new Box(h, w);
return res;
}
}
static void Main(string[] args) {
Box b1 = new Box(14, 3);
Box b2 = new Box(5,7);
Box b3 = b1 + b2;
Console.WriteLine(b3.Height);
Console.WriteLine(b3.Width);
}

Here’s what happens when the code above is executed, starting with the Main class:

  • First, a new object of the Box class is instantiated called b1.
  • Within the Box class, a publicly available constructor method is automatically executed taking the first value it receives, 14, and assigns it to a new, integer-based variable called h, and the second value it receives, 3, and assigns it to a new, integer-based variable called w.
  • Then, the integer-based property Height is called, from the beginning of the Box class, and it gets the value from where it was called, which was h, which is equal to 14, and then sets it (the Height property) equal to 14.
  • Next, the integer-based property Width is called, from the beginning of the Box class, and it gets the value from where it was called, which was w, which is equal to 3, and then sets it (the Width property) equal to 3.
  • Back to the Main class, another new object of the Box class is instatiated called b2.
  • Within the Box class, a publicly available constructor method is automatically executed taking the first value it receives, 5, and assigns it to a new, integer-based variable called w.
  • Then, the integer-based property Height is called, from the beginning of the Box class, and it gets the value from where it was called, which was h, which is equal to 5, and then sets it (the Height property) equal to 5.
  • Next, the integer-based property Width is called, from the beginning of the Box class, and it gets the value from where it was called, which was w, which is equal to 7, and then sets it (the Width property) equal to 7.
  • Back to the Main class, another new object of the Box class is instantiated called b3.
  • This time, however, it’s value is set to be the result of b1 + b2. Within the Box class is a method that defines the overloading operator +, which requires it to receive two objects of type Box, and it will name the first one a and the second one b.
  • Next, a new integer-based variable is declared called h, which is equal to the value of the Height property of the Box object called a, plus the value of the Height property of the Box object called b. In other words, h is equal to 14 + 5, or h = 19.
  • After that, a new integer-based variable is declared called w, which is equal to the value of the Width property of the Box object called a, plus the value of the Width property of the Box object called b. In other words, w is equal to 3 + 7, or h = 10.
  • A new object of the Box class is instantiated called res, and is sent the current values for h and w, which are 19 and 10 respectively.
  • Within the Box class, a publicly available constructor method is automatically executed taking the first value it receives, 19, and assigns it to a new, integer-based variable called h, and the second value it receives, 10, and assigns it to a new, integer-based variable called w.
  • Then, the integer-based property Height is called, from the beginning of the Box class, and it gets the value from where it was called, which was h, which is equal to 19, and then sets it (the Height property) equal to 19.
  • Next, the integer-based property Width is called, from the beginning of the Box class, and it gets the value from where it was called, which was w, which is equal to 10, and then sets it (the Width property) equal to 10.
  • Returning to where it was called, the next line of code returns the current Box object and it’s associated properties back to where it was called.
  • Back within the Main method, the next line prints to the screen, the value of the Height property of the Box object called b3, which is 19.
  • After that, the next line prints to the screen, the value of the Width property of the Box object called b3, which is 10.

A couple of final points that the SoloLearn app makes is that all arithmetic and comparison operators can be overloaded. That said, for example, when overloading the greater than operator, the less than operator should be defined as well.

That brings us to the end of another tough, but worth it, journey. Next time, we’ll jump into the next section called Inheritance & Polymorphism. Until then, please let me know if anything I wrote isn’t clear (or isn’t right).

Also, here’s a link to the previous article, Learning to Code — Part 8a: More on Classes.

Web: BIMuzer.com

Twitter: BIMuzer

LinkedIn: Scott Rosenbloom

--

--