The concept of encapsulation revolves around the nation that an object's data should not be directly accessible from an object instance. Rather, class data is defined as private. If the object user wants to alter the state of an object, it does so indirectly using public members.
Note: Members of a class that represent an object's state should not be marked as public.
You should get in the habit of defining private date, which is indirectly manipulated using one of 2 main techniques.
- You can define a pair of public accessor (get) and mutator (set) methods.
- You can define a public property.
Whichever technique you choose, the point is that a well-encapsulated class should protect its data and hide the details of how it operates from the prying eyes of outside world. This is often termed black-box programming.
Encapsulation Using Traditional Accessors and Mutator
A traditional approach is to define an accessor (get method) and a mutator (set method). The role of a get method is to return to the caller the current value of the underlying state data. A set method allows the caller to the change the current value of the underlying state data, as long as the defined business rules are met.
class Employee
{
private string _employee;
public string GetName() => _employee;
public void SetName(string name) => _employee = name;
}Encapsulation Using Properties
.NET Core languages prefer to enforce data encapsulation state data using properties.
class Employee
{
private string _empName;
public string Name
{
get => _empName;
set
{
if (value.Length > 15)
{
Console.WriteLine("Error! Name length 15");
}else
{
_empName = value;
}
}
}
}A C# property is composed by defining a get scope (accessor) and set scope (mutator) directly within the property itself.
Within a set scope of a property, you use a token named value, which is used to represent the incoming value used to assign the property by the caller. This token is not a true C# keyword but is what is known as a contextual keyword. When the token value is within the set scope of the property it always represents the value being assigned by the caller, and it will always be the same underlying data type as the property itself.
Read-Only Properties
To do so, simply omit the set block.
public string SocialSecurityNumber
{
get
{
return _empSSN;
}
}
Write-Only Properties
Omit the get block.
public int Id
{
set
{
_empId = value;
}
}
Mixing Private and Public Get/Set Methods on Properties
If the goal is to prevent the modification of the number from outside the class, then declare the get method as "public" but the set method as private like this:
public string SocialSecurityNumber
{
get _empSSN;
private set => _empSSN = value;
}Defining Static Properties
class SavingsAccount
{
private static double _currInterestRate = 0.04;
// static prop
public static double InterestRate
{
get => _currInterestRate;
set => _currInterestRate = value;
}
}Property Patterns (C# 8.0)
This feature allows you to match an object's properties against specific values or sub-pattern. It evaluates whether an object is of a certain type and checks if its properties meet the cretieria you defined.
class Employee
{
public string Name { get; set; }
public Department Department { get; set; }
}
class Department
{
public string Name { get; set; }
static void Main(string[] args)
{
Employee emp = new Employee();
// Property Patterns
if(emp is Employee { Department: { Name : "IT"} })
{
Console.WriteLine("Ok");
}
}
}Extended Property Pattern (C# 10.0)
This is a syntax improvement over regular property patterns. When dealing with nested objects, instead of nesting multiple curly braces { }, it allows you to drill down into child properties directly using the dot (.) notation.
// Extended Property Pattern
if(emp is Employee { Department.Name:"" })
{
Console.WriteLine("Ok");
}Understanding Automatic Properties
class Car
{
private string carName = string.Empty;
public string PetName
{
get
{
return carName;
}
set
{
carName = value;
}
}
}To streamline the process of providing simple encapsulation of field data, you may use automatic property syntax. As the name implies, this feature will offload the work of defining a private backing field and related C#property member to the compiler using a new bit of syntax.
class Car
{
public string PetName { get; set; }
}When defining automatic properties, you simply specify the access modifier underlying data type, property name and empty get/set scoped. At compile time, your type will be provided with an auto-generated private backing field and a fitting implementation of the get/set logic.
Note : The name of the auto generated private backing field is not visible within your C# code base. The only way to see it is to make use of a tool such as ildasm.exe
// Read-Only Prop
public string MyProp { get; }
// Write-Only Prop
public int MyProp { set; }Automatic Properties and Default Values
When you use automatic properties to encapsulate numerical or bool data, you are able to use the autogenerated type properties straight away within your code base, as the gidden backing fields will be assigned a safe default value (false for booleans and 0 for numerical data).
However, be aware that if you use automatic property syntax to wrap another class variable, the hidden private reference type will also be set to a default value of null.