C# Encapsulation Tutorial

In this tutorial we learn how to restrict access to classes and their members by using access modifiers like public, private and protected.

We also cover accessor and mutator methods (getters and setters) and how to split a class into sections with partial.

What is encapsulation

One of the three core principles, or pillars, in any object oriented program is encapsulation. Encapsulation is where we hide unnecessary implementation details from the object.

As an example, think of a class that translates a document into another language.

The class would encapsulate the inner details of loading, manipulating and closing the file. We don’t need to worry about how it loads the document behind the scenes.

All we do is instantiate the class, and send it the appropriate commands, like: Open a file from this location, save file to that location etc.

Encapsulation also provides us with data protection. We can’t accidentally change how a file is manipulated from outside the class if it’s properly protected.

How to encapsulate a class with access modifiers

To encapsulate members of our class, we need to use certain access modifiers. These are keywords that specify the level of access, or protection, class members have.

The table below shows the available access modifiers:

ModifierMeaning
publicNo access restriction. Can be accessed from an object or any derived class. Can be accessed from external assemblies.
privateCan only be accessed from within the class (or struct) that defines it.
protectedCan only be accessed from within the class that defines it as well as any child class.
internalCan only be accessed from within the current assembly.
protected internalCan only be accessed from within the current class, or derived classes, inside the current assembly.

The public access modifier

The public access modifier means the member can be accessed from objects, external assemblies (DLL’s) and any derived classes (classes that inherit from this one).

Syntax:
public varType varIdentifier;

public funcType funcIdentifier(parameterList)
{
    // function body
}
Example:
using System;

namespace Classes
{
    class Program
    {
        static void Main(string[] args)
        {
            Access a = new Access();

            Console.WriteLine(a.message);
            a.Message();

            Console.ReadLine();
        }
    }

    class Access
    {
        public string message = "Public field";

        public void Message()
        {
            Console.WriteLine("Public function");
        }
    }
}

In the example above, our public members can be accessed outside the class.

The private access modifier

When the private modifier is applied to class members, they can only be accessed inside their current class.

When we create a new object, we cannot access a private member through that object. For example, if the attribute name is private, we can’t use object.name.

Syntax:
private varType varIdentifier;

private funcType funcIdentifier(parameterList)
{
    // function body
}

The private access modifier is the default modifier and doesn’t need to be specified explicitly.

Syntax:
varType varIdentifier;

funcType funcIdentifier(parameterList)
{
    // function body
}
Example:
using System;

namespace Classes
{
    class Program
    {
        static void Main(string[] args)
        {
            Access a = new Access();

            a.Message(); // Compiler error

            Console.ReadLine();
        }
    }

    class Access
    {
        private string message1 = "Private field.";
        private string message2 = "No access outside of this class";

        void Message()
        {
            Console.WriteLine(message1 + " " + message2);
        }
    }
}

In the example above, we can use private members inside the Access class. The compiler will raise an error when we try to access the member outside of the class.

The protected access modifier

A protected member can only be accessed by the class it’s currently in, as well as any class that inherits from it (child classes).

Syntax:
protected varType varIdentifier;

protected funcType funcIdentifier(parameterList)
{
    // function body
}
Example:
using System;

namespace Classes
{
    class Program
    {
        static void Main(string[] args)
        {
            AccessChild a = new AccessChild();

            a.ModifiedMessage();

            Console.ReadLine();
        }
    }

    class Access
    {
        protected string message1 = "Protected field.";
        protected string message2 = "Access inside this and child class";

        protected void Message()
        {
            Console.WriteLine(message1 + " " + message2);
        }
    }

    class AccessChild : Access
    {
        public void ModifiedMessage()
        {
            Console.WriteLine("Accessed from parent:");
            Console.WriteLine(message1 + " " + message2);
        }
    }
}

In the example above, we create a child class that derives from the class Access and instantiate a child class object. The child class has access to the two protected fields of its parent class.

Accessor and Mutator methods (getters and setters)

Accessor and mutator methods are ways for us to interact with the private and protected members of a class. They are also referred to as getters and setters.

Syntax:
private fieldType fieldIdentifier;

public fieldType GetField()
{
    return fieldIdentifier;
}

public void SetField(fieldType parameter)
{
    fieldIdentifier = parameter;
}
Example:
using System;

namespace Classes
{
    class Program
    {
        static void Main(string[] args)
        {
            Access a = new Access();

            // Get the default value
            Console.WriteLine(a.GetMessage());

            // Set the value
            a.SetMessage("Hello there");

            // Get the modified value
            Console.WriteLine(a.GetMessage());
            Console.ReadLine();
        }
    }

    class Access
    {
        private string message = "Hello World";

        public string GetMessage()
        {
            return message;
        }

        public void SetMessage(string message)
        {
            this.message = message;
        }
    }
}

In the example above, we use the public get and set methods to access and modify the private field in the Access class.

Accessor and Mutator (getter and setter) properties

The accessor and mutator properties are just a shorthand way of writing the functions. All .NET languages prefer data encapsulation with properties.

Syntax:
private fieldType fieldIdentifier;

public fieldType fieldIdentifier
{
    // Accessor
    get { return fieldIdentifier; }

    // Mutator
    set { fieldIdentifier = value; }
}

In the set property, the value is the actual keyword value and will represent the value passed to it.

Example:
using System;

namespace Classes
{
    class Program
    {
        static void Main(string[] args)
        {
            Access a = new Access();

            // Get the default value
            Console.WriteLine(a.Message);

            // Set the value
            a.Message = "Hello there";
            Console.WriteLine(a.Message);

            Console.ReadLine();
        }
    }

    class Access
    {
        private string message = "Hello World";

        public string Message
        {
            get { return message; }
            set { message = value; }
        }

    }
}

In the example above, we first get the default private value and print it to the console, this value cannot be changed. To change the value we assign a new value to the property, in this case Message.

Accessor and Mutator (getter and setter) auto-implemented properties

Properties are designed to be shorthand for functions. However, we can use a similar shorthand for attributes.

Syntax:
public fieldType fieldIdentifier { get; set; }

There is no field. When the compiler sees the auto implemented properties, it automatically creates the private field for us internally. The private field it creates is set to its default value.

Example:
using System;

namespace Classes
{
    class Program
    {
        static void Main(string[] args)
        {
            Access a = new Access();

            // Get the default value
            // string default is an empty string
            Console.WriteLine(a.Message);

            // Set the value
            a.Message = "Hello there";

            // Get the modified value
            Console.WriteLine(a.Message);
            Console.ReadLine();
        }
    }

    class Access
    {
        public string Message { get; set; }
    }
}

In the example class above, we create the string Message property. The compiler automatically creates a string field for it internally with its default value, which is an empty string. We then modify the empty string to have a value and print it to the console.

Partial classes

Some class members, such as fields, constructors etc. will often stay the same throughout the lifetime of the class. Functions on the other hand, tend to be modified often with updated algorithms.

In C# we can partition a single class across multiple code files to isolate code that doesn’t change that often.

To split a class into parts, we separate the class and use the partial keyword on all the parts.

Syntax:
partial class Identifier
{
    // class body
}

partial class Identifier
{
    // class body
}
Example:
using System;

namespace Classes
{
    class Program
    {
        static void Main(string[] args)
        {
            Access a = new Access();

            Console.WriteLine(a.message);
            a.Message();

            Console.ReadLine();
        }
    }

    partial class Access
    {
        public string message = "A field in one part of the same class";
    }

    partial class Access
    {
        public void Message()
        {
            Console.WriteLine("A function in another part of the same class");
        }
    }
}

In the example above, we split the class into two parts. One contains a field, the other contains a function.

Nothing changes in the way we use the class, the only change is in how we define the class.

Partial class use cases

Partial classes are only used when a class becomes excessively large. However, it’s good practice in general, to try and keep classes short. Somewhere around 250 to 300 lines is okay.

Visual Studio actually uses partial classes all the time. When we’re building a GUI, for example, Visual Studio places all the designer-generated code into a dedicated partial class file.

Summary: Points to remember

  • Encapsulation is when we define the access level of our classes.
    • The public access modifier doesn’t impose any restrictions.
    • The private access modifier only allows members to be accessed within the current class, not outside of it (through an object).
    • The protected access modifier allows the current class, and any child classes that inherit from it, to access its members.
  • Accessor and mutator methods are used to access and modify private and protected members.
    • Getter and setter properties is a shorthand for writing the methods.
    • Auto implemented properties are used on attributes.
  • Partial classes allow us to split a class into sections. This only affects the definition of the class.