Skip to main content

C# Properties

Introduction

Properties are a fundamental feature in C# that implement encapsulation - one of the core principles of object-oriented programming. Properties provide a flexible mechanism to read, write, or compute the values of private fields. They can be used as if they are public data members, but they are actually special methods called accessors. This enables data to be accessed easily while still promoting safety and flexibility of methods.

In this tutorial, you'll learn:

  • What properties are and why they're important
  • How to create and use properties
  • Different types of properties in C#
  • Best practices for implementing properties

Understanding Properties

Why Use Properties?

Before diving into properties, let's understand why they exist. Consider this class with public fields:

csharp
public class Person
{
public string name; // Public field, directly accessible
public int age; // Public field, directly accessible
}

Using public fields like this creates several problems:

  • No validation: Anyone can set age to a negative number
  • No control: Fields can be changed without any restrictions
  • No flexibility: If you later need to calculate or modify how data is accessed, you'd break existing code

Properties solve these issues by acting as intermediaries between your private data and the outside world.

Basic Property Syntax

A basic property consists of:

  1. A private field (the actual storage)
  2. A public property (the access point)
  3. Get and set accessors (the gatekeepers)

Here's the syntax:

csharp
public class Person
{
// Private field - stores the actual data
private string _name;

// Public property - provides access to the data
public string Name
{
get { return _name; }
set { _name = value; }
}
}

Here, _name is our private storage. The Name property provides controlled access to it through get and set accessors. The keyword value represents the value being assigned during a set operation.

Using Properties

Let's see how to use the properties we just created:

csharp
class Program
{
static void Main()
{
Person person = new Person();

// Using the set accessor
person.Name = "John";

// Using the get accessor
Console.WriteLine(person.Name); // Output: John
}
}

To the outside world, using a property looks exactly like using a field, but behind the scenes, our getter and setter methods are running.

Property Types

Auto-Implemented Properties

Since simple get/set properties are so common, C# offers a shorthand syntax:

csharp
public class Person
{
// Auto-implemented property
public string Name { get; set; }
}

With auto-implemented properties, C# automatically creates the private field for you. This is perfect when no additional logic is needed in getters and setters.

Read-Only Properties

You can create properties that can only be read but not modified from outside the class:

csharp
public class Person
{
private DateTime _birthDate;

public Person(DateTime birthDate)
{
_birthDate = birthDate;
}

// Read-only property
public int Age
{
get
{
return DateTime.Today.Year - _birthDate.Year;
}
}
}

This example shows a read-only property that calculates age based on a birth date. It doesn't have a setter because age isn't something you should directly set - it's computed from the birth date.

Write-Only Properties (Rare)

Though uncommon, you can create write-only properties by providing only a setter:

csharp
public class User
{
private string _password;

// Write-only property
public string Password
{
set { _password = HashPassword(value); }
}

private string HashPassword(string input)
{
// Hash the password
return "hashed_" + input;
}
}

Properties with Validation

Properties shine when you need to validate data before storing it:

csharp
public class Person
{
private int _age;

public int Age
{
get { return _age; }
set
{
if (value < 0)
{
throw new ArgumentException("Age cannot be negative");
}
_age = value;
}
}
}

Now if anyone tries to set a negative age:

csharp
Person person = new Person();
person.Age = -5; // Throws ArgumentException

Expression-Bodied Properties

For simple property implementations, you can use expression body syntax (introduced in C# 6.0):

csharp
public class Rectangle
{
public double Length { get; set; }
public double Width { get; set; }

// Expression-bodied read-only property
public double Area => Length * Width;
}

This shorter syntax is especially useful for calculated properties.

Auto-Implemented Properties with Initializers

In C# 6.0 and later, auto-implemented properties can have initializers:

csharp
public class Product
{
// Auto-property with an initializer
public string Category { get; set; } = "General";
public decimal Price { get; set; }
}

Init-Only Properties

C# 9.0 introduced init-only properties that can only be set during object initialization:

csharp
public class Person
{
public string Name { get; init; }
public DateTime BirthDate { get; init; }
}

You use them like this:

csharp
var person = new Person 
{
Name = "Maria",
BirthDate = new DateTime(1985, 3, 25)
};

// This would cause a compile-time error:
// person.Name = "Changed"; // Error: Init-only property

Real-World Example: Product Inventory System

Let's see how properties help us create a robust product inventory system:

csharp
public class Product
{
// Private backing fields
private string _name;
private decimal _price;
private int _stockQuantity;

// Properties with validation
public string Name
{
get { return _name; }
set
{
if (string.IsNullOrWhiteSpace(value))
throw new ArgumentException("Product name cannot be empty");
_name = value;
}
}

public decimal Price
{
get { return _price; }
set
{
if (value < 0)
throw new ArgumentException("Price cannot be negative");
_price = value;
}
}

public int StockQuantity
{
get { return _stockQuantity; }
set
{
if (value < 0)
throw new ArgumentException("Stock cannot be negative");
_stockQuantity = value;
}
}

// Calculated property
public bool IsInStock => StockQuantity > 0;

// Read-only property with logic
public string Status
{
get
{
if (StockQuantity > 10) return "Available";
if (StockQuantity > 0) return "Low Stock";
return "Out of Stock";
}
}
}

Using this class:

csharp
class Program
{
static void Main()
{
try
{
Product laptop = new Product();
laptop.Name = "Laptop Pro";
laptop.Price = 1299.99m;
laptop.StockQuantity = 5;

Console.WriteLine($"Product: {laptop.Name}");
Console.WriteLine($"Price: ${laptop.Price}");
Console.WriteLine($"Available: {laptop.IsInStock}");
Console.WriteLine($"Status: {laptop.Status}");

// This would throw an exception:
// laptop.StockQuantity = -3;
}
catch (ArgumentException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}

Output:

Product: Laptop Pro
Price: $1299.99
Available: True
Status: Low Stock

This example demonstrates how properties help us create a robust system:

  1. Data validation prevents invalid values
  2. Calculated properties (IsInStock) derive values from other properties
  3. Complex logic can be encapsulated in properties (Status)

Best Practices for Properties

  1. Use meaningful names: Properties should have clear, descriptive names
  2. Follow conventions: Use PascalCase for property names (FirstName not firstName)
  3. Consider immutability: Make properties read-only when they shouldn't change
  4. Validate input: Check input in setters to maintain object validity
  5. Keep accessors simple: Avoid complex or time-consuming operations in getters
  6. Use auto-implemented properties when no additional logic is needed
  7. Consider init-only properties for immutable objects

Summary

Properties in C# provide a powerful way to implement encapsulation while maintaining simple syntax for accessing object data. They offer:

  • Protection of data through controlled access
  • Validation to ensure data integrity
  • The ability to compute values on-the-fly
  • A clean, field-like syntax for method-like functionality

By mastering properties, you'll create more robust, maintainable C# code that follows best practices in object-oriented programming.

Exercises

  1. Create a BankAccount class with properties for AccountNumber (read-only) and Balance (with validation to prevent negative values)
  2. Build a Temperature class that stores temperature in Celsius but provides properties to get/set it in Fahrenheit
  3. Implement a Student class with auto-implemented properties for name and ID, and a calculated property for determining if they are passing (GPA >= 2.0)
  4. Create a Password class with a write-only property that hashes passwords and a method to verify passwords

Additional Resources



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)