Containment or composition is the process of adding another class as a member of a class. For example, a Person class can have a field of type Name. The program below shows an example of a class using containment.

using System;
 
namespace ContainmentDemo
{
    class Name
    {
        public string FirstName { get; set; }
        public string LastName { get; set; }
 
        public Name(string f, string l)
        {
            FirstName = f;
            LastName = l;
        }
    }
 
    class Person
    {
        private Name myName;         
 
        public Name MyName           
        {                            
            get { return myName; }   
            set { myName = value; }  
        }                            
 
        public Person(Name name)                              
        {                                                     
            myName = new Name(name.FirstName, name.LastName); 
        }                                                     
 
        public override string ToString()
        {
            return myName.FirstName + " " + myName.LastName;
        }
    }
 
    class Program
    {
        public static void Main()
        {
            Person person1 = new Person(new Name("John", "Smith"));
 
            Console.WriteLine(person1.ToString());
        }
    }
}

Example 1 – Containment Demo

John Smith

We declared a class named Name that will serve as a field in another class   (Lines 5-15). This class has a constructor that accepts the first name and the last name of a person. Those values will be added to their corresponding properties.

class Person
{
    private Name myName;

    public Name MyName
    {
        get { return myName; }
        set { myName = value; }
    }

    public Person(Name name)
    {
        myName = new Name(name.FirstName, name.LastName);
    }

    public override string ToString()
    {
        return myName.FirstName + " " + myName.LastName;
    }
}

The Person class contains a field having a type of Name and its corresponding property. It will store the name of this Person object. Notice that the constructor accepts a  Name object, and then used that name object to initialize the myName field. This is known as aggregation. We also override the ToString() method of the System.Object class to show the full name when this method is called.

class Program
{
    public static void Main()
    {
        Person person1 = new Person(new Name("John", "Smith"));

        Console.WriteLine(person1.ToString());
    }
}

We now create a Person object. We used the constructor that accepts a Name object as an argument. We declared the Name object directly inside the parentheses and we also provided the first name and last name as arguments to the Name constructor. Finally, we printed the results using the customized ToString() method.

Also, note that classes can have objects having its own type. For example, a Person object can have a Person member. Consider the code below.

class Person
{
   public Person Sibling { get; set; }
   public string Name { get; set; }
}

You can see that a Person object was declared inside the Person class. So every time you create a Person object or instance, that object has another Person object inside it.

Person person1 = new Person();
person1.Sibling = new Person();
person1.Name = "John Smith";
person1.Sibling.Name = "Mike Smith";

The above code demonstrated how to access and initialize that Person member. Since the Sibling property is also a Person object, it too has a Sibling object inside it. So you can provide as many siblings as you want for person1 like this:

person1.Sibling.Sibling = new Person();
person1.Sibling.Sibling.Name = "Franc Smith";
person1.Sibling.Sibling.Sibling = new Person();
person1.Sibling.Sibling.Sibling.Name = "Bob Smith";
//And so on...