There are two kinds of relationships between classes:
- Inheritance (is-a)
- Composition (has-a)
This post is based on an instructional video by Mosh Hamedani at Udemy.com. Suppose we are building an application that has many parts to it. One of the parts, a logger, is responsible for logging information to a text file. We have two other parts of the application that need to use the logger. We have a DbMigrator and we have an Installer. Both of these have a relationship to the logger class. They both use it. This is nothing new here in C#. We don’t need any special syntax like we need with inheritance. One class contains another. We can say that a car has an engine. The engine is inside the car.
Generally, developers favour composition over inheritance. For an example of inheritance we have the post called C# Interfaces Extensibility.
With composition we get code re-use, as we get with inheritance. Composition is more flexible and a means to designing loosely coupled applications. Here is a UML diagram showing our example project.

First, here is our logger code. It just has a method that writes to the console so we can see that it was executed.
class Logger
{
public void Log(string message) {
Console.WriteLine("Log it: " + message);
}
}
Here is the Installer. Notice that we have a constructior that takes a Logger.
class Installer
{
private readonly Logger _logger;
public Installer(Logger logger) {
_logger = logger;
}
public void Install() {
_logger.Log("installing...");
}
}
Here is the DbMigrator.
class DbMigrator
{
private readonly Logger _logger;
public DbMigrator(Logger logger) {
_logger = logger;
}
public void Migrate() {
_logger.Log("we are migrating...");
}
}
Below is our program. Notice that the first three lines of code create a dbMigrator and an Installer. In the first of the three lines, we create a logger object and pass it all in one line of code. In the second line of code, we create a new Logger object called logger. In the third line, we pass the logger object.
namespace Composition
{
class Program
{ // Mosh Hamedani at Udemy.com
static void Main(string[] args)
{
var dbMigrator = new DbMigrator(new Logger());
var logger = new Logger();
var installer = new Installer(logger);
dbMigrator.Migrate();
installer.Install();
}
}
}
A Private Field and a Constructor
In our two classes that use the logger, they each have a private field that contains the logger. That field is of the type of our class: Logger. To say it again, they have a private field that contains the custom class logger. Also, they each have a constructor that we wrote. The constructor requires a logger object that is used to initialize the private field. The Logger is a private field in the Installer and the DbMigrator.
The related class is a private filed in the composite class.
using System;
namespace Composition
{
class Program
{ // Mosh Hamedani at Udemy.com - code has been modified.
static void Main(string[] args)
{
var logger = new Logger();
logger.dateTime = DateTime.Now; // current date and time
var dbMigrator = new DbMigrator(logger);
dbMigrator.Migrate();
logger.dateTime = DateTime.Now; // current date and time
var installer = new Installer(logger);
installer.Install("programA");
logger.dateTime = DateTime.Now; // current date and time
installer.Location = "D:\\Program Files"; // to D: not C:
installer.Install("programB");
}
}
}