Skip to content

Instantly share code, notes, and snippets.

@jv-amorim
Last active April 26, 2020 20:32
Show Gist options
  • Save jv-amorim/db4741715bd0e55158929fc3a035a9d9 to your computer and use it in GitHub Desktop.
Save jv-amorim/db4741715bd0e55158929fc3a035a9d9 to your computer and use it in GitHub Desktop.
Notes about Orient-Object Programming in C#.

📌️ C# OOP Notes

Content

  • OOP;
  • Fields x Properties x Attributes;
  • Four Pillars of OOP;
  • Constructor Chaining;
  • Types of Relationships;
  • Class types;
  • Access modifiers;
  • Extension methods;
  • Dependency Injection.

💡️ OOP:

✅️ Object and Class: A detailed explanation can be found here.

✅️ Entity: Something that is relevant to the application.

✅️ Steps to define the application classes:

Requirements -> Analyze the business problem -> Start with the nouns -> Define appropriate member -> Classes.

✅️ Secrets of Reuse:

Build it once; Test it; Reuse it; Update it in one place.

✅️ Advantages of Reuse:

Reduces amount of code; Reduces development time; Reduces costs; Reduces bugs.

✅️ Static modifier on a class member denotes that the member belongs to the class itself rather than to any specific instance. It's useful for tracking information shared between all the instances of the class. A instancied object cannot access a property that have the static modifier. This property can be accessed only using <ClassName>.<PropertyName>.

✅️ Every class in C# .NET inherits from the Object class, that is a .NET class where all other classes declared derives from. This class have some public methods: Equals(), GetHashCode(), GetType(), ToString(). All these methods can also be overridden using the override modifier in other classes.

✅️ If I want to create a method that receives as parameter a list of any type, that is, a list with generic type, just that I put the type Object as the type of the list. Example: public void SomeFunction(List<Object> myList){ ... }.


🤔️ Fields x Properties x Attributes:

✅️ Field:

private string _myName;
public float myWeight;

✅️ Properties:

public int MyAge { get; set; }
public string MyName
{
	get
	{
		return _myName;
	}
	set
	{
		_myName = value;
	}
}

✅️ Attributes:

[Serializable]			// This is an attribute.
public class MyClass
{
	...
}

🏛️ Four Pillars of OOP:

✅️ Abstraction:

Abstraction describes an entity in simple terms, ignoring the irrelevant details. It reduces complexity by focusing only on what is important for the purpose of this specific application. Abstraction also help us map the business requirements to an initial set of classes.

✅️ Encapsulation:

Encapsulation allows the hiding of data and the implementation within the class, keeping they protected and only accessible through a public programming interface.

✅️ Inheritance:

Inheritance help us leverage reuse. A more detailed explanation of inheritance can be found below in the "Types of Relationships" topic.

✅️ Polymorphism:

Polymorphism is an impressive sounding word that basically means many shaped. In object-oriented programming, polymorphism is the concept that a single method, such as the ToString method, can behave differently depending on the type of object that calls it.

Example: If the base class calls ToString, ToString displays the full class name. If an order object calls ToString, it displays the order date and ID. If a customer object calls ToString, it displays the customer's full name, and if a product object calls ToString, it displays the product name.

So a method can have different shapes. The application determines which shape of the method to use at the time of execution based on the type of instance that called it. This demonstrates inheritance-based polymorphism, whereby a base class defines a method, and any derived class can override that method to provide its own implementation, basically providing its own shape for the method. Polymorphism allows us to work with groups of classes in a uniform way.

Override modifier on a class method indicates an override for a method that was inherited from the parent class. Before a base class method can be overridden, that method must be defined as abstract or virtual. An abstract method is a placeholder with no implementation. Use it if the base class does not have a default implementation for the method such as in our validate example. The method must then be overridden in the derived class. Abstract methods can only be used in an abstract class. A virtual method has a default implementation and can be used in an abstract or a concrete class. The method can be overridden in the derived class if needed.


🔗️ Constructor Chaining:

When the class have a overload of constructors, it's possible to chain two or more constructors together (sometimes to avoid code repetition).

  1. Before the chaining:
public Customer()
{

}

public Customer(int customerId)
{
	CustomerId = customerId;
	AddressList = new List<Address>();
}
...
  1. After the chaining (the first constructor, with no parameters, after his execution, will call the second constructor (passing the customerId parameter equal to 0)):
public Customer() : this(0)
{

}

public Customer(int customerId)
{
	CustomerId = customerId;
	AddressList = new List<Address>();
}
...

👩‍❤️‍👨️ Types of Relationships:

✅️ Collaboration 🤝️: Collaboration, or "uses a", defines a relationship where one object collaborates with, or uses, another object that is not otherwise related. For example, you may collaborate with a coworker to complete a task. Recognize a collaborative relationship when you see a class use an instance of an otherwise unrelated class to perform an operation.

✅️ Composition 🎂️: A composition, or "has a" relationship exists whenever an object from one class is composed of one or more objects from another class. The customer address relationship is an example of this type. How do we implement these relationships? Composite objects often contain references to their constituent objects as properties. Another way to implement compositions is using an ID property: usually used if is wanted to retain the relationship, but don't always need all of the related object's data. These relationships also have a cardinality, that is to say they have a one-to-one, one-to-many, or many-to-many relationship.

✅️ Inheritance 👨‍👧‍👦️: Inheritance ("is a") provides a mechanism for defining classes that are a more specialized version of another class. For example, we already have a Customer class. We could create additional classes that define more specific types such as a business customer class, residential customer class, and so on. The Customer class in this example is referred to as the parent or base class. Each specialized class is called a child or derived class because it is a derived special case of the class above it in the hierarchy. Each child class inherits, or takes on, the members of its parent class. In our example, the business customer class inherits all of the members of the Customer class, including the customer name, email address, and so on. The specialized business customer class would only need to implement the additional features for the specific type. In this way, the specific classes leverage reuse in that they are using the properties and methods already defined in the parent class. Each class in a lower branch of the hierarchy has an is a relationship to the class above it in the hierarchy. So a business customer is a customer and inherits all of the properties and behaviors defined for the customer, a residential customer is a customer, and so on. If the requirements warranted it, we could expand the hierarchy even further and define local government customers, and state government customers, and so on. When implementing inheritance in C#, a class can only have one parent class. C# does not support multiple inheritance. There can be any number of inheritance levels. From customer, to educator, to high school, to teacher, and so on. Defining subtypes using an inheritance hierarchy can be helpful when first thinking about the classes and their relationships, but they are not always useful when defining the classes to implement. Only build an inheritance relationship between classes if the derived classes have specialized properties or behaviors that require unique code.

To make a class inheriting from another class in C#, just put a colon after the child class name and write the parent class name:

public class Child : Parent
{
	...
}

🗂️ Class types:

✅️ Abstract Class:

An abstract class is an incomplete class with at least one property or method that has not been implemented. Because an abstract class is incomplete, it can't be instantiated. An abstract class is intended for use as a base class, and not used on its own. Define an abstract class using the abstract keyword.

✅️ Concrete Class:

A concrete class is a normal class. A concrete class can be instantiated. A concrete class can be used as a base class or on its own. When creating a class, a concrete class is created by default. No additional keyword is required.

✅️ Sealed Class:

A sealed class is a class that cannot be extended through inheritance. Sealing a class prevents extension and customization. If you build a class and want to ensure that no other classes extend or override its functionality, consider marking it as sealed. Seal a class using the sealed keyword.

✅️ Static Class:

A static class cannot be instantiated. Since there is no instance variable, you access the members of a static class by using the class name itself. A static class contains only static members, that is, all members must have the static keyword in declaration. This type of class is sealed and also cannot contain constructors.


🔒️ Access modifiers:

  • Public: any class can access the property.
  • Private: only the base class can access this property.
  • Protected: the base class and all derived classes can access this property.

🌎️ Extension methods:

An extension method allows us to add methods to any existing type without the source code of the type, without inheritance, without recompiling, without any changes at all to the original type. Since we aren't modifying the original type, extension methods are great for adding methods to .NET types such as string. Adding methods to a .NET type means that the type appears in IntelliSense with all the built-in methods for that type. From the perspective of the code using the method, it looks like the method is a member of the extended type. Only static methods in static classes can be extension methods. To change a static method to an extension method, we simply add the "this" keyword before the first parameter.

  1. Before being an extension method:
	// (Creating the method:)

	public static class StringHandler
	{
		public static string InsertSpaces(string source)
		{
			...
		}
	}

	// (Using the method:)

	string nameWithSpaces = StringHandler.InsertSpaces(name);

  1. After being an extension method:
	// (Creating the method:)

	public static class StringHandler
	{
		public static string InsertSpaces(this string source)
		{
			...
		}
	}

	// (Using the method:)

	string nameWithSpaces = name.InsertSpaces();

💉️ Dependency Injection:

The Dependency Injection is a set of software design principles and patterns that enable us to develop loosely coupled code. Example of usage:

  1. Before using dependency injection:
public class DatabaseMainController
{
	...

	public readonly SQLiteConnection database;

	public DatabaseMainController()
	{
		database = new SQLiteConnection(databasePath, password);
	}

	...
}


public class UsersController
{
	...

	private readonly SQLiteConnection database;

	public UsersController()
	{
		database = new SQLiteConnection(databasePath, password);
	}
}

private void Start()
{
	DatabaseMainController dbController = new DatabaseMainController();
	dbController.CreateTable<Users>();

	UsersController = new UsersController();
	userController.InsertNewData(...);

	...
}
  1. After using dependency injection:
public class DatabaseMainController
{
	...

	public readonly SQLiteConnection database;

	public DatabaseMainController()
	{
		database = new SQLiteConnection(databasePath, password);
	}

	...
}

public class UsersController
{
	...

	private readonly SQLiteConnection database;

	public UsersController(DatabaseMainController dbController) ⚠️
	{
		database = dbController.database;
	}
}

private void Start()
{
	DatabaseMainController dbController = new DatabaseMainController();
	dbController.CreateTable<Users>();

	UsersController = new UsersController(dbController); ⚠️
	userController.InsertNewData(...);

	...
}

Explanation: in the code above (from a Unity application), the DatabaseMainController class have is responsible for making the connection to the database. In the first code (1) the DatabaseMainController also are instantiated when we instantiate a UsersController object. Instead of creating two instances of the DatabaseMainController, we can force the use of just one DatabaseMainController with a dependency injection. In the second code (2), with the dependency injection created, the instantiation of a UsersController needs a DatabaseMainController object as a parameter in his contructor and, instead of creating a new DatabaseMainController object, the UsersController object only receives the reference from the DatabaseMainController created previously. With this operation, we have a code with less coupling (since the instantiation of DatabaseMainController is not made inside of the UsersController constructor) and the responsabilities are more segregated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment