CLASSES INTRODUCTION

With classes in C# C sharp we approach the heart of Object Oriented programming also known as OOP. In any object-oriented programming language, classes are fundamental entities. To approach this type of programming we will simulate the management of a checking account. Obviously the real code would be quite different, but to introduce OOP we will focus on this type of example. Now let’s see how to declare a class in C#.

using System;
namespace DefinizioneClasse
{
    class MyClass //DEFINIZIONE DI UNA CLASSE
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

DEFINITION OF A CLASS

As you see you use the keyword class followed by the class name in pascal case notation first letter capitalized and the initial of each word always capitalized. It is not mandatory to follow this convention, however it is the standard convention followed for class names in C#. As we will see classes can contain attributes which are nothing more than class instance variables or class variables if we used the static keyword , methods which are the actions performed on the attributes, in the example of managing a bank account there will surely be two methods Prelievo and Accredito that go to modify the saldo attribute.

INSTANCE VARIABLES

So far we have seen the definition and declaration of local variables, that is, variables defined as parameters of a method or defined internally to the method. These variables exist as long as the method where they were declared is running then they are removed from memory as we have seen. Instance variables are variables defined at the class level and represent attributes of that class.
Suppose we define three variables at the class level myInt e myString e myBoolean. The default access modifier will be private, that is, these variables are visible only within the class in which they are declared and inaccessible from outside.

using System;
namespace DefinizioneClasse
{
    class MyClass //DEFINIZIONE DI UNA CLASSE
    {
        //DEFINIAMO TRE ATTRIBUTI DELLA CLASSE
        int myInt;
        string myString;
        bool myBoolean;
    }

    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

CREATION OF AN INSTANCE

We create an instance of MyClass, you can consider a class as a template from which objects of that class will then be created also called instances of the class.

MyClass mc = new MyClass();

This is done by using the keyword new, the variable mc is called an instance variable, and that object variable will have its own copy of the integer myInt, the string myString, and myBoolean. Such variables that we find in the mc instance are called instance variables and such an object will be able to handle them as it wishes. Unlike local variables, instance variables are initialized to 0 for numeric types, false for Boolean types, and null for other objects that may be contained in the class.

ACCESS MODIFIERS

Access modifiers regulate visibility at the class, method, attribute and property level. By default the attributes and methods of a class have a private access modifier so they are not visible outside the class and accessible from other classes.
So write string myString; or private string myString; takes on the same meaning. Conversely, the modifier public makes the variable accessible from the outside so other objects could change the value of the attribute. This is an inadvisable procedure because it violates information hiding. To handle the attribute properly you make it private and write two public methods that allow you to display and write it, obviously after doing all the checks to the value that is passed in from outside.

using System;
namespace DefinizioneClasse
{
    class MyClass
    {
        private string myString;
        public string GetValue()
        {
            return myString;
        }
        public void SetValue(string s)
        {
                 //Si fanno i controlli del caso, si manipola se necessario
                 //la stringa passata e come ultima operazione la si assegna
                 //a myString. 
                myString = s;
        }   

    }

    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

The third modifier we consider, after public and private is internal. This access modifier is the default for classes so write class MyClass() o internal class MyClass() is the same thing. The modifier internal can be used on attributes, methods and classes with this keyword in fact we make methods classes and attributes visible only to the assembly they belong to, so to all objects defined in the assembly but not outside it.

THE STATIC KEYWORD

The variables in a class fall into two broad categories.

  • Instance variables.
  • Class variables.

If we define an object from the definition of MyClass with the keyword new

MyClass mc = new MyClass();

The mc object created from the class definition has three instance variables myInt, myString and myBoolean (see code above) that go to form the state of the object at a certain time. If we declare ten objects of the class MyClass each will have its own copy of the instance variables and each will have its own internal state. Conversely, using the keyword static variable will be defined only once in the class MyClass and all instances of MyClass will share the same variable. These types of variables are called class variables and are accessed with dot notations.

using System;
namespace DefinizioneClasse
{
    class MyClass
    {
        
        static int myInt;
        static void MyMethod()
        {
            myInt=90;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

To access the class variable myInt requires a static method i.e., a method that is also a class method therefore defined with the static keyword. To access myInt we use Dot Notations ie: MyClass.myInt = 90; Conversely, an instance variable must not have the keyword static, an instance variable and an instance method is defined below.

using System;
namespace DefinizioneClasse
{
    class MyClass
    {
        
        int myInt;
        void MyMethod()
        {
           //IMPOSTA LA VARIABILE DI ISTANZA
            myInt=90;
        }
    }
    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

STATIC CLASSES AND STATIC CONSTRUCTORS

A class can also be declared as static, in which case you are explicitly indicating to the compiler that this class will contain only class members, variables and methods and you will not be able to construct objects (instances) from this class. These types of classes are used when you need a set of general-purpose methods that will be called directly on the class; it would not make sense to create objects for these types of classes. I anticipate the next topic, we have not yet talked about constructors, a constructor is a method that takes the same name as the class and is used to fabricate objects. When dealing with a static constructor, the compiler calls it once when the class is invoked at runtime. Its job is to initialize static variables.

using System;
namespace DefinizioneClasse
{
    static class MyClass 
    {
        static int myInt;
        static MyClass()
        {
            myInt = 150;
        } 
    }
    class Program
    {
         static void Main(string[] args)
        {
            
        }
    }
}

THE CONSTRUCTOR

Constructors are methods that mandatorily have the same name as the class; they do not return values but are used to properly initialize attributes defined within the class. Let’s take an example:

using System;
namespace DefinizioneClasse
{
    class Persona
    {
        private string nome;
        private string cognome;
        private int eta;
        public Persona(string p_nome, string p_cognome, int p_eta)
        {
            nome = p_nome;
            cognome = p_cognome;
            eta = p_eta;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

As you see, I have defined a Persona class with three instance attributes with private modifier and the constructor with public access modifier so that we can create instances of the class from other parts of the code. But let’s see how to create instances of the Persona class, a topic already anticipated earlier.

using System;
namespace DefinizioneClasse
{
    class Persona
    {
        private string nome;
        private string cognome;
        private int eta;
        public Persona(string p_nome, string p_cognome, int p_eta)
        {
            nome = p_nome;
            cognome = p_cognome;
            eta = p_eta;
        }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Persona p = new Persona("Mario","Rossi",54);//CREAZIONE ISTANZA DELLA CLASSE PERSONA 
        }
    }
}

As you see to create person class objects first you declare the type, then the variable reference p the keyword new and the constructor with the parameters that initialize the instance variables of the Person class. With this object we cannot do much since it contains only three instance variables, but it is important to understand the role of the constructor, which as I repeat is used to give initial state to the instance variables, and how it should be invoked to construct instances of the Person class.

THE SCOPE OF VISIBILITY OF THE VARIABLES

There are three scopes or scope of visibility of the variables which are as follows:

  • Class scope
  • Method scope
  • Block scope
using System;
namespace DefinizioneClasse
{
    class Persona
   {
        //LE VARIABILI DICHIARATE A LIVELLO DI CLASSE HANNO UN CLASS SCOPE
        private string nome;
        private string cognome;
        private int eta;
        public Persona(string p_nome, string p_cognome, int p_eta)
        {
            //METHOD SCOPE
            nome = p_nome;
            cognome = p_cognome;
            eta = p_eta;
            if (p_nome == "Mario")
            {
                //BLOCK SCOPE
                string s = "Ciao Mario!";
                Console.WriteLine(s);
            }
        }
    }


    class Program
    {
        static void Main(string[] args)
        {
            
        }
    }
}

Instance variables have a scope of visibility throughout the class in which they are defined, first name, last name, eta are accessible within the constructor method. The variables p_name, p_last name, p_eta have a scope at the constructor level, they are varIable local to the method therefore only usable within the constructor method. The variable s is defined inside an if block its scope of visibility is at the if block level, exiting that block the variable s will no longer be accessible.

THE NAME HIDING

  • FIRST RULE
using System;
namespace DefinizioneClasse
{
    class Program
    {
        public static void Main(string[] args)
        {
            int x = 20;
            while(true)
            {
                    int x = 10; //ERRORE
                    Console.WriteLine(x);
                    break; 
            } 
            Console.WriteLine(x);
        }
    }
}

A variable x defined within a method has scope throughout the method in which it was defined, if we try to declare a new variable x at the while block level we get a compile error.

  • SECOND RULE
using System;
namespace DefinizioneClasse
{
    class MyClass {
        int x = 10;
        public void MyMethod()
        {
               int x = 20;
               Console.WriteLine(x); //20
        } 
    }

    class Program
    {
        public static void Main(string[] args)
        {
            
        }
    }
}

The variable inside the method temporarily hides the instance variable. This is what is meant by name hiding. In other methods of the class where the variable x is not redefined you continue to see the instance variable set to 10.

  • THIRD RULE
using System;
namespace DefinizioneClasse
{
    class Program
    {
        public static void Main(string[] args)
        {
            for(int x = 0; x<;10; x++) 
            {
                Console.WriteLine(x);
            }
        }
    }
}

The variable x is visible only within the for loop code block and possibly other nested blocks.

THE KEYWORD this

this represents the current instance.

using System;
namespace DefinizioneClasse
{
    class Persona
    {
        private string nome;
        private string cognome;
        private int eta;
        public Persona(string nome, string cognome, int eta)
        {
            this.nome = nome;
            this.cognome = cognome;
            this.eta = eta;
        }
    }

    class Program
    {
        public static void Main(string[] args)
        {
            
        }
    }
}

By prepending the keyword this there is no longer ambiguity between the names of variables local to the constructor and instance variables this.nome, this.cognome, and this.eta. In a class we can define more than one constructor as long as it has a different signature, that is, a different number of parameters. In this case we speak of constructor overloading.

using System;
namespace DefinizioneClasse
{
    class Persona
    {
            private string nome;
            private string cognome;
            private int eta;
            public Persona(string nome, string cognome, int eta)
            {
                this.nome = nome;
                this.cognome = cognome;
                this.eta = eta;
            }
            //RICHIAMA IL COSTRUTTORE A TRE PARAMETRI
            public Persona(string cognome, int eta):this("Mario", cognome, eta)
            {
                this.cognome = cognome;
                this.eta = eta;
            }
            //CHIAMA IL COSTRUTTORE A TRE PARAMETRI
            public Persona() :this("Mario", "Rossi", 41){}
    }


    class Program
    {
        public static void Main(string[] args)
        {
            
        }
    }
}

We have created a new two-parameter constructor with which we intend to create objects from the class Person.
To do this we set the name with a default value of “Mario” and call the three-parameter constructor with the following syntax:

this(“Mario“, cognome, eta)

Where this represents the current instance. It is also possible to create a constructor that accepts no parameters, in which case we must call the three-parameter constructor using the syntax just seen and providing default values so that the object is initialized correctly.
We can also define no constructor in the class, neither with parameters nor without parameters in which case the compiler behind the scenes will generate one without parameters.This is called constructor by default and allows us to create instances from a class while not having a constructor defined. This is particularly useful in some situations.

using System;

namespace EsercitazioneClassi
{
    internal class Program
    {
        private static void Main(string[] args)
        {
            var utente1 = new Utente("1234560", "Mario", "Rossi", "4356789");
            utente1.SetSaldo(13000m);
            var utente2 = new Utente("12345678", "Mario", "Verdi", "435679879");
            utente2.SetSaldo(30000m);
            utente1.SaldoCorrente();
            utente2.SaldoCorrente();
            var b1 = new Banca("1234567890", "UBI Banca");
            var b2 = new Banca("1234576543", "UBI Banca");
            b1.Preleva(5000m,utente1);
            b2.Preleva(10000m,utente2);
            utente1.SaldoCorrente();
            utente2.SaldoCorrente();
            b1.Accredito(5000m,utente1);
            utente1.SaldoCorrente();
        }
    }

    internal class Utente
    {
        private string cognome;
        private string id;
        private string nome;
        private string numeroConto;
        private decimal saldo;

        public Utente(string id, string nome, string cognome, string numeroConto)
        {
            this.id = id;
            this.nome = nome;
            this.cognome = cognome;
            this.numeroConto = numeroConto;
        }

        internal void Denominazione()
        {
            Console.WriteLine($"Utente {this.nome + " " + this.cognome}");
        }

        internal void SaldoCorrente()
        {
            Console.WriteLine($"Utente {this.nome + " " + this.cognome} saldo: {GetSaldo()}");
        }

        internal decimal GetSaldo()
        {
            return saldo;
        }

        internal void SetSaldo(decimal importo)
        {
            if (importo < 0)
            {
                Console.WriteLine($"Saldo negativo. Utente:{this.nome + " " + this.cognome}");
                return;
            }

            saldo = importo;
        }
    }

    internal class Banca
    {
        private string abi;
        private string cab;
        private string denominazione;
        private string id;
        private Utente utente;

        public Banca(string id, string denominazione, string abi, string cab)
        {
            this.id = id;
            this.denominazione = denominazione;
            this.abi = abi;
            this.cab = cab;
        }

        public Banca(string id, string denominazione) :
            this(id, denominazione, "06098", "14400")
        {
        }

        internal void Denominazione()
        {
            Console.WriteLine($"Id Banca {this.id} ,denominazione: {this.denominazione}");
        }

        internal void Preleva(decimal importo,Utente utente)
        {
            this.utente = utente;
            var saldo = this.utente.GetSaldo();
            if (importo > saldo)
            {
                Console.WriteLine($"Operazione di prelievo non ammessa, importo richiesto {importo}");
                this.utente.Denominazione();
                return;
            }

            if (importo < 0)
            {
                Console.WriteLine($"Importo prelevato non valido: {importo}");
                return;
            }

            this.utente.SetSaldo(this.utente.GetSaldo() - importo);
            //IMPOSTO A NULL L'UTENTE
            this.utente = null;
        }

        internal void Accredito(decimal importo, Utente utente)
        {
            this.utente = utente;
            if (importo < 0)
            {
                Console.WriteLine($"Importo accreditato non valido: {importo}");
                this.utente.Denominazione();
                return;
            }

            this.utente.SetSaldo(this.utente.GetSaldo() + importo);
            //IMPOSTO A NULL L'UTENTE
            this.utente = null;
        }
    }
}

IN-DEPTH STUDY OF CLASSES

In C#, classes are one of the fundamental units of object-oriented programming. Classes serve as templates for creating objects (instances of classes) and can contain fields, properties, methods, events, and constructors. Here is an overview of classes in C# with examples for each of the fundamental elements:

Defining a Class

To define a class in C#, you use the keyword class followed by the class name. Here is an example of a simple class:

public class Persona
{

      // Fields

      private string nome;
      private int eta;

      // Properties

      public string Nome
      {

          get { return name; }

          set { name = value; }
      }

     public int Eta
    {

         get { return eta; }

         set { eta = value; }
     }

      // Constructor

     public Persona(string nome, int eta)
     {

       this.nome = nome;

       this.eta = eta;
     }

     // Methods

    public void Saluta()
    {

       Console.WriteLine($“Hello, my name is {nome} and I am {eta}.”);

   }
}

Creating an Instance

To create an instance of a class, you use the keyword new followed by the constructor of the class:

Persona persona = new Persona(“Mario Rossi,” 30);

persona.Saluta(); // Output: Hello, my name is Mario Rossi and I am 30 years old.

Fields

Fields are variables declared directly within a class. They can be private, protected or public. In the example above, nome and eta are fields.

Properties

Properties allow you to control access to fields. They are similar to variables but include accessory methods(get) and mutators(set).
In the example above, Nome and Eta are properties.

Methods

Methods are functions defined within a class. They can manipulate the fields of the class and can be invoked on instances of the class.
The Saluta method in the example above is an example of a method.

Constructors

Constructors are special methods used to initialize new instances of a class. They have the same name as the class and return no value. The constructor of the class Persona in the example above is an example of a constructor.

Inheritance

C# supports inheritance, which allows you to create a new class based on an existing class. The new class inherits the members of the base class but can add new members or overwrite existing ones. We will see Property, Inheritance and Polymorphism in detail in later posts.

public class Studente: Persona
{

        public string Scuola { get; set; }

        public Studente(string nome, int eta, string scuola)
        : base(nome, eta)
        {

           Scuola = scuola;

        }

        public void Studia()
        {

           Console.WriteLine($”{Nome} study at {Scuola}.”);

        }
}

Studente studente = new Studente(“Anna Bianchi“, 20,“University of Rome“);

studente.Saluta(); // Output: Hello, my name is Anna Bianchi and I am 20 years old. studente.Studia(); // Output: Anna Bianchi is studying at University of Rome.

Polymorphism

Polymorphism allows objects of a derived class to be treated as objects of their base class. It is frequently used with virtual methods and overrides.

public class Insegnante: Persona
{
          public string Materia { get; set; }

          public Insegnante(string nome, int eta, string materia)
         : base(nome, eta)
          { 

             Materia = materia;

          }

          public override void Saluta()
          {

              Console.WriteLine($“Good morning, I am Professor {Nome} and I teach {Materia}.”);

          }
}

Persona persona1 = new Studente(“Giorgio Verdi“, 21,“Politecnico di Milano“);
Persona persona2 = new Insegnante(“Maria Rossi,” 40,“Math“);

persona1.Saluta(); // Output: Hello, my name is Giorgio Verdi and I am 21 years old.
persona2.Saluta(); // Output: Hello, I am Professor Maria Rossi and I teach Mathematics.

Summary

Classes in C# are powerful tools for modeling real-world entities and organizing code. With fields, properties, methods, constructors, inheritance and polymorphism, classes allow you to create flexible and maintainable programs.

LINKS TO PREVIOUS POSTS

LINK TO THE CODE ON GITHUB