Skip to content

Instantly share code, notes, and snippets.

@asarnaout
Last active January 25, 2018 17:11
Show Gist options
  • Save asarnaout/4c6e51ab052edac8aa093eeefc6433d8 to your computer and use it in GitHub Desktop.
Save asarnaout/4c6e51ab052edac8aa093eeefc6433d8 to your computer and use it in GitHub Desktop.
Object Oriented Basics in C#
/*
* In this gist we will discuss OOP basics, this topic will cover:
*
* -Classes, properties, attributes, and methods.
* -Access modifiers.
* -Static vs Non-static methods.
* -Exceptions
* -Casting
*/
/*
* In Object Oriented Programming a class represents an entity in your code. Classes can have attributes,
* properties, methods, events, operators and indexers defined within them. All of these members represent
* the building blocks of the class and how this class can interact with other classes.
*/
public class Person
{
public string Identifier; //This is an Attribute
public string Name { get; set; } //This is a property
/*
* Attributes and properties represent values that will define a class. A property is the same thing as
* an attribute, however notice that properties have get and set accessors which get compiled to getter
* and setter functions. Thus properties are equivalent to attributes with getter and setter functions.
*
* In the above example, the getter and setter functions are empty, however this is not always the case
* and you can implement some logic (example: validation) within these functions as follows:
*/
private DateTime _dateOfBirth;
public DateTime DateOfBirth
{
get { return _dateOfBirth; }
set
{
if (value.Year < 1900 || value.Year > 2018)
{
throw new Exception("Invalid birthdate");
}
_dateOfBirth = value;
}
}
/*
* Access modifiers allow you to control the accessibility of your types and members. In C# we have four
* access modifiers:
*
* -private: Indicates that the type or member is accessible only within the same class.
* -protected: Indicates that the type or member is accessible only within the same class or sub-classes.
* -internal: Indicates that the type or member is accessible only within the same assembly (project).
* -public: Indicates that the type or member is accessible from anywhere within the solution.
*/
private void SomePrivateMethod()
{
//This method can be called only by members of the same class
}
protected void SomeProtectedMethod()
{
//This method can be only called from the class Person and from any other class which inherits from Person
}
public void SomePublicMethod()
{
//This method can be called from anywhere
}
/*
* Methods can be polymorphic, which means that they can have the same name but receive different parameters
* as follows:
*/
public void Method1()
{
}
public void Method1(string arg)
{
}
/*
* A constructor is a function that is responsible for returning new instances of the class in which they
* are defined in. If you do not define a constructor in your class, then C# will automatically create an
* empty constructor (that takes no parameters). A constructor is called when you use the 'new' keyword
* as follows:
*
* Person p1 = new Person();
*
* The 'new' keyword will call Person's constructor and return a new instance of 'Person' and store its
* value in the variable p1.
*/
public Person() //This is an empty constructor
{
}
public Person(string identifier, string name) //This is a parameterful constructor
{
this.Identifier = identifier;
this.Name = name;
/*
* Since the constructor is responsible for creating a new instance of the class, therefore the
* keyword 'this' when used inside the constructor will refer to the object being created (i.e: the
* Person being created) and thus in this example, the constructor is creating a new Person and setting
* its Identifier and Name values to be equal to the passed parameters
*/
}
/*
* You can have as many constructors as you would like taking different parameters:
*/
public Person(string identifier, string name, DateTime dob)
{
this.Identifier = identifier;
this.Name = name;
this.DateOfBirth = dob;
}
/*
* In C#, all members and methods can be either static or non-static (instance). The difference between
* a static member and a non-static member is that a static member does not require an object of its
* defining class to be created while a non-static member does require an object.
*/
public static void SomeStaticMethod()
{
/*
* This method can be only called as follows:
* Person.SomeStaticMethod();
*/
}
public void SomeInstanceMethod()
{
/*
* This method can be only called as follows:
* Person p1 = new Person();
* p1.SomeInstanceMethod();
*/
}
}
/*
* In this example, we will set the class 'Employee' to inherit from the class 'Person'. Note that in C#, a
* class is allowed to inherit from only one class (i.e: multiple inheritance is not allowed). It is also worthy
* to note that all classes by default inherit the System.Object class which defines methods such as .ToString().
*
* +--------+
* | Object |
* +--------+
* |
* v
* +--------+
* | Person |
* +--------+
* |
* v
* +----------+
* | Employee |
* +----------+
*
* Inheritance means that a sub-class will inherit all non-private members (methods/attributes/properties..etc)
* from its parent class.
*/
public class Employee : Person
{
public int Salary { get; set; }
/*
* The attribute 'Salary' is accessible only to objects of type Employee, however it is not accessible
* to objects of type 'Person':
*
* Person p1 = new Person();
* int s = p1.Salary; //This will result in a compilation error
*
* However:
*
* Employee e1 = new Employee();
* int s = e1.Salary; //This will run normally.
*/
public void GetInfo()
{
Console.WriteLine("Name: " + base.Name);
/*
* The keyword 'base' refers to the parent class, note that the Employee object was able to access
* the 'Name' Property defined in its parent class.
*/
Console.WriteLine("Salary: " + this.Salary);
}
}
public class ExceptionsDemo
{
public static void SomeApi()
{
/*
* Programs are highly likely to run into exceptions, these exceptions can cause a halt to the program
* flow and may hurt performance and result in a bad user experience. Developers can safe-guard their
* code using try-catch-finally blocks as follows:
*/
try
{
/*
* If the execution runs into an exception while running code defined inside a 'try' block then it
* will be automatically directed to the relevant 'catch' statement.
*/
Console.WriteLine("Doing Something");
Console.WriteLine("Doing Something Else");
throw new OutOfMemoryException(); //At this point, the execution will be directed to the first catch statement
Console.WriteLine("Unreachable code");
}
catch (OutOfMemoryException e)
{
//Do some logging here
}
catch (NullReferenceException e)
{
}
finally
{
/*
* The finally statement can be used to further safe-guard your code, code executed in the finally
* block will be ALWAYS executed whether an exception was thrown in the try statement or not and
* will be also executed even if an exception was thrown in the catch statement itself.
*/
Console.WriteLine("This will always execute");
}
}
}
public class CastingDemo
{
public static void Main(string[] args)
{
/*
* In our example, we've created a 'Person' class and an 'Employee' subclass where Employee inherits
* from 'Person', which means that:
*
* An Employee IS a Person
*
* However:
*
* A Person IS NOT an Employee
*
* Therefore we are allowed to cast an Employee object to a Person, however we are not allowed to do
* the opposite. (Casting is the act of converting an object from one type to another).
*
* The casting rule of thumb is that you can cast a child to its parent however you cannot cast a
* parent to its child.
*/
Employee employee = new Employee();
Person p1 = (Person) employee; //This code will run normally.
Person person = new Person();
Employee e1 = (Employee) person; //Exception
/*
* The above line will result in an InvalidCastException since you cannot convert a base type to a sub
* type (You cannot convert a parent to a child).
*
* Note that you are only allowed to cast from/to classes that have a relationship defined between them.
*/
string str = (string) person; //Compilation error
/*
* The above line will result in a compilation error since there is no parent/child relationship defined
* between the class 'String' and the class 'Person' therefore the cast operation is not allowed.
*
* Note that Casting can be also done implicitly (without using brackets) as follows:
*/
Person castedPerson = new Employee();
/*
* In the above example, a new Employee object was created and was implicitly casted to a Person object
* without using brackets (Using brackets to cast objects is called explicit casting).
*
* Note that implicitly casting from a parent type (Person) to a child type (Employee) will result in a
* compilation error (explicit casting won't result in a compilation error but will result in an exception
* as demonstrated previously):
*/
Employee invalidEmployee = new Person(); //Compilation error
/*
* Developers who prefer to safe-guard their code from invalid cast exceptions can use try-catch statements
* to safe guard their code
*/
Person per1 = new Person();
try
{
Employee emp1 = (Employee) per1;
}
catch (InvalidCastException e)
{
//If the cast is invalid, the catch statement will run
}
/*
* However, since having many exceptions in your program can harm your application's performance, therefore
* the keyword 'is' was introduced in C# to check whether an object can be casted to a type or not
*/
bool canCast = per1 is Employee; //This value will be equal to false since a Person object cannot be casted to an Employee
if (canCast)
{
Employee castedEmp = (Employee) per1;
}
/*
* Some developers prefer not to use the keyword 'is' since it might be a bit inferior from a performance
* perspective to check the parent/child relationship twice (once while using the is keyword and another while performing
* the casting operation itself).
*
* To mitigate the above issue, the keyword 'as' was introduced to safely cast an object to another type
* within one statement:
*/
Employee castedEmployee = per1 as Employee;
/*
* The keyword 'as' will try to cast 'per1' to an Employee object, if the cast is successful, then the
* casted employee object is returned, otherwise, null is returned. And thus using 'as' we can safely cast from
* one type to another without having performance issues and without the risk of running into exceptions
*/
if (castedEmployee != null)
{
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment