loading...

c# basics -encapsulation , properties- manual,auto-implemented, readonly ,static

j523 profile image Vidisha Parab ・5 min read

Object oriented programming (OOP) focuses on modelling a business problem through objects. These objects are instances of a "Class". A class thus serves as a blueprint to create objects from. A class apart from being a piece of code, carries data and behavior the object must posses.The data fields are referred to as properties and the behavior are called as methods , together they are referred as class members. An important point to focus on here is one of the pillars of the OOP -Encapsulation. To encapsulate means to confine or encase something. In OOP we encapsulate a class by hiding its members. Here it is obvious to think, if we want to keep members hidden then why create the class in the first place ? However, when we create a class we have the liberty to expose the members. We decide which members should be accessible to the outside world , in view of this a class is also called as a "Contract". We are familiar with the access modifiers like private ,protected and public, which we decorate our class methods with. What about the data fields or the class properties ? Well, we expose them through getters and setters.Consider a class Employee, if we were to define a data field which holds an employee's name then the most basic way to do it manually as follows
Alt Text

In here we are exposing the field _employeeName through the public property EmployeeName in Employee (class) contract . We see two pieces of information,

get { }; set { };

. And mind you these are the most powerful tools we could use for our benefit. Since we are not directly exposing our data field _employeeName , we have a great level of control to validate the value we set for this at a field level ! For e.g. we could write much complex logic in the set { } block. We will get to it soon.
Another way to implement properties is to use the "autoimplemented" version provided by visual studio (when you use the snippet- prop. type prop and press tab).We use auto-implemented properties when we don't have to write any logic for getting or setting the data field - no validations or logic to get in the way- just plain simple accessing and setting the property. However an important thing to note here, behind the scenes the compiler creates a backing field for them as a default. Same can be seen for the

public int TimeHours { get; set; }

. Below is the IL (Intermediate Language) code for TimeHours

Alt Text

We could also make our properties read-only , the only thing we need to do is to have a private set;
Say you have to calculate the pay for an employee. This salary depends on the hours the employee puts in and their designation. We don't want any external agent to set this value and our class is solely responsible to do this internally . We can achieve that through the following

 public class Employee
    {
        private string _employeeName;

        public string EmployeeName
        {
            get
            {
                return _employeeName;
            }
            set
            {
                _employeeName = value;
                ;
            }
        }

        public int TimeHours { get; set; }
        public Designation EmployeeDesignation { get; set; }
        public double EmployeePay
        {
            get => CalculateEmployeePay();
            private set { }
        }

        private double CalculateEmployeePay()
        {
            switch (this.EmployeeDesignation)
            {
                case Designation.JuniorDeveloper: return TimeHours * 100;
                case Designation.MidLevelDeveloper: return TimeHours * 200;
                case Designation.SeniorDeveloper: return TimeHours * 300;
                default: return 0;
            }

        }
    }

    public enum Designation
    {
        JuniorDeveloper,
        MidLevelDeveloper,
        SeniorDeveloper
    }

Note that we have made the "setter" of property EmployeePay private thus making it read-only. We also introduced a logic in here by calling the method CalculateEmployeePay in the "getter" to return the employee's salary.

So far what we have discussed are "instance properties". They are specific to the object instance and are not shared between instances. If you recall, we said the class is a blueprint to create objects.These are also known as instances.

            Employee emp1 = new Employee();
            emp1.EmployeeName = "Saania";
            emp1.EmployeeDesignation = Designation.MidLevelDeveloper;
            emp1.TimeHours = 10;

            Employee emp2 = new Employee();
            emp2.EmployeeName = "Hormaz";
            emp2.EmployeeDesignation = Designation.JuniorDeveloper;
            emp2.TimeHours = 2;

In the above snippet emp1 and emp2 are two separate instances of the Employee class with each having their own set of class members (properties+methods). If I make change to the EmployeeName property of the instance emp1 then that is local to emp1 and would not reflect in emp2. But what if there is a requirement that the Employee class holds a count for the total employees. Now this data would be global to an organization, we cannot have local copies for it. Say emp1 is our first employee and after instantiating it we do something like

emp1.TotalEmployeeCount += 1

then this change will be only confined to emp1 .When we create emp2 a fresh instance would be created setting all its data-members to their defaults (effectively if we had a property TotalEmployeeCount then for emp2 its value would be 0 at the beginning) i.e. the effect of emp1 is totally lost. In such scenarios we opt for static members
Static properties are common between instances. Any change made to them is global. Also one thing to note is , the static properties cannot be accessed through the instances unlike the rest where we use the "." (dot) operator e.g

emp2.EmployeeName = "Hormaz";

. They are accessed through the class name. Let us create a static property which will help us maintain a count for all the employees.

public static int TotalEmployees { get; set; }

. If we have to access them then we will do that as

Employee.TotalEmployees += 1;

It is also important to note that static data is not thread safe. Say this Employee class is a part of a larger distributed systems network. There are systems which modify the TotalEmployees count.It may happen so that two different systems try to modify the data at the same time. An obvious question would come to our mind that it is an atomic operation, we are just incrementing its count by one. We will go back to the autoimplemented properties concept. We saw that it is just a syntactic sugar for the manual property with a backing field. The compiler does in fact creates a backing field for these properties. If we were to modify a property in essence it would be

  1. read the value through "getter"
  2. increment the value by 1
  3. set the value through "setter"

It no longer is an atomic operation. There is a vulnerability that another thread (another process) gets access to its value before the previous one could increment and set the updated value. This leads to garbage data.

Posted on by:

Discussion

markdown guide