Properties for Encapsulation
Part of the Object Oriented Programming section of Coddy's C# journey — lesson 32 of 70.
Properties are the primary tool for implementing encapsulation in C#. Instead of exposing fields directly, you wrap them with properties that control how data is accessed and modified.
The pattern combines a private field with a public property that includes validation or logic in its accessors:
public class Temperature
{
private double celsius;
public double Celsius
{
get { return celsius; }
set
{
if (value < -273.15)
celsius = -273.15; // Absolute zero minimum
else
celsius = value;
}
}
}Now external code can't set an impossible temperature. The property acts as a gatekeeper, ensuring your object always stays in a valid state. This is encapsulation in action - the internal data is protected while still being accessible through a controlled interface.
You can also use properties to compute values or trigger side effects:
public double Fahrenheit
{
get { return celsius * 9 / 5 + 32; }
set { celsius = (value - 32) * 5 / 9; }
}The outside world sees two properties, but internally there's only one field. Properties let you change implementation details without breaking code that uses your class - a key benefit of proper encapsulation.
Challenge
EasyLet's build a product inventory system that uses properties to protect data integrity. You'll create a class where prices can never go negative and quantities are always kept within valid bounds - demonstrating how properties act as gatekeepers for your data.
You'll organize your code across two files:
Product.cs: Create aProductclass in theInventorynamespace that manages product information with validation through properties. Your product needs:- A private field
price(decimal) with a publicPriceproperty that ensures the price never goes below0- if someone tries to set a negative price, it should be stored as0instead - A private field
quantity(int) with a publicQuantityproperty that keeps values between0and1000- values below 0 become 0, values above 1000 become 1000 - A public auto-implemented property
Name(string) - A computed read-only property
TotalValuethat returnsPrice * Quantity
- A private field
Program.cs: In your main file, create aProductusing input values. Then attempt to modify the price and quantity with additional input values that might be invalid. Print the product's state after creation and after each modification to see how the properties protect the data.
You will receive five inputs:
- Product name
- Initial price (decimal)
- Initial quantity (integer)
- New price to set (decimal, might be negative)
- New quantity to set (integer, might be out of range)
Print the output in this format:
After creation:
{Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}
After modifications:
{Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}For example, if the inputs are Laptop, 999.99, 50, -100, and 1500, the output should be:
After creation:
Laptop: Price=999.99, Qty=50, Total=49999.50
After modifications:
Laptop: Price=0, Qty=1000, Total=0Notice how the negative price became 0 and the quantity of 1500 was clamped to 1000. Your properties are protecting the object from invalid states - this is encapsulation at work!
Cheat sheet
Properties wrap private fields with controlled access through get and set accessors, enabling validation and encapsulation:
public class Temperature
{
private double celsius;
public double Celsius
{
get { return celsius; }
set
{
if (value < -273.15)
celsius = -273.15;
else
celsius = value;
}
}
}Properties can compute values based on internal fields:
public double Fahrenheit
{
get { return celsius * 9 / 5 + 32; }
set { celsius = (value - 32) * 5 / 9; }
}Read-only properties omit the set accessor, allowing only retrieval of computed or stored values.
Try it yourself
using System;
using Inventory;
class Program
{
public static void Main(string[] args)
{
// Read inputs
string name = Console.ReadLine();
decimal initialPrice = Convert.ToDecimal(Console.ReadLine());
int initialQuantity = Convert.ToInt32(Console.ReadLine());
decimal newPrice = Convert.ToDecimal(Console.ReadLine());
int newQuantity = Convert.ToInt32(Console.ReadLine());
// TODO: Create a Product using the initial values
// TODO: Print the product state after creation
Console.WriteLine("After creation:");
// Print format: {Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}
// TODO: Modify the price and quantity with the new values
// TODO: Print the product state after modifications
Console.WriteLine("After modifications:");
// Print format: {Name}: Price={Price}, Qty={Quantity}, Total={TotalValue}
}
}
This lesson includes a short quiz. Start the lesson to answer it and track your progress.
All lessons in Object Oriented Programming
1Fundamentals of OOP
External FilesNamespaces & DirectivesIntro to Classes & ObjectsThe 'this' KeywordMethods and ParametersFields vs PropertiesConstructorsObject InitializersRecap - Simple Calculator4Inheritance
Basic Inheritance (:) SyntaxThe 'base' KeywordVirtual & Override KeywordsSealed ClassesThe 'object' Base ClassRecap - Employee Hierarchy7Advanced Features
Operator OverloadingIndexers (this[])ToString() OverrideExtension MethodsRecap - Custom List2Properties & Static Members
Auto-Implemented PropertiesRead/Write-Only PropertiesStatic Fields & MethodsStatic ClassesExpression-Bodied Members5Polymorphism & Interfaces
Compile vs Runtime PolyInterface vs Abstract ClassMultiple InterfacesExplicit InterfacesUpcasting & DowncastingRecap - Shape Calculator3Class Architecture
Instance vs Static Data'readonly' & 'const' KeywordsBacking FieldsRecap - Bank Account Manager6Encapsulation
Access ModifiersProperties for EncapsulationData Hiding ImplementationImmutability PatternsRecap - Student Records