Last active
January 25, 2018 17:11
-
-
Save asarnaout/4c6e51ab052edac8aa093eeefc6433d8 to your computer and use it in GitHub Desktop.
Object Oriented Basics in C#
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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 | |
*/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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(); | |
*/ | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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