Class in C#
A class is a user-defined type that groups together data (fields and properties) and behaviour (methods) into one unit. It is the most important building block in C#: almost everything you write, from a console program to an ASP.NET Core controller, lives inside a class.
Think of a class as the blueprint of a house. The blueprint isn't a house; it describes how every house of that design looks. From one blueprint you can build many real houses, each with its own colour and owner. In C#, the blueprint is the class, and each house built from it is an object.
The parts of a class
| Member | What it is | Example |
|---|---|---|
| Field | A variable that stores the object's data, usually private | private decimal _balance; |
| Property | Controlled access to data, with get and/or set | public string Holder { get; } |
| Constructor | Special method that runs when an object is created, to set its starting values | public BankAccount(string holder) { ... } |
| Method | An action the object can perform | public void Deposit(decimal amount) |
| Static member | Belongs to the class itself, shared by all objects | public static int TotalAccounts |
| Others | Events, indexers, nested types, operators | public event EventHandler Changed; |
A simple class
The smallest useful class just holds some data in properties and has a method:
public class Student
{
public int StudentId { get; set; }
public string Name { get; set; } = string.Empty;
public int Age { get; set; }
public string Mobile { get; set; } = string.Empty;
// Constructor: runs when we write "new Student(...)"
public Student(int studentId, string name, int age, string mobile)
{
StudentId = studentId;
Name = name;
Age = age;
Mobile = mobile;
}
public void PrintDetails()
{
Console.WriteLine($"{StudentId}: {Name}, {Age} years, {Mobile}");
}
}
// Creating objects (instances) of the class
var riya = new Student(1, "Riya", 21, "9876543210");
var aman = new Student(2, "Aman", 22, "9123456780");
riya.PrintDetails();
aman.PrintDetails();
One class, two objects, each with its own data. The new keyword creates an object in memory and calls the constructor.
A realistic class: a bank account
Real classes do more than hold data: they protect it. A bank account balance must never be changed directly from outside, only through Deposit and Withdraw, which check the rules. This is encapsulation, one of the pillars of object-oriented programming.
public class BankAccount
{
// Field: private, so no code outside the class can change it directly
private decimal _balance;
// Static property: one value shared by ALL accounts
public static int TotalAccounts { get; private set; }
// Read-only properties: set once in the constructor
public string AccountNumber { get; }
public string Holder { get; }
// Computed property: read-only view of the private field
public decimal Balance => _balance;
// Constructor with an optional parameter
public BankAccount(string accountNumber, string holder, decimal openingBalance = 0)
{
AccountNumber = accountNumber;
Holder = holder;
_balance = openingBalance;
TotalAccounts++;
}
public void Deposit(decimal amount)
{
if (amount <= 0)
throw new ArgumentException("Deposit must be positive.");
_balance += amount;
}
public void Withdraw(decimal amount)
{
if (amount > _balance)
throw new InvalidOperationException(
$"Insufficient balance in {AccountNumber}. Available: {_balance}");
_balance -= amount;
}
// Override the ToString method inherited from System.Object
public override string ToString() => $"{AccountNumber} | {Holder} | Balance: {_balance}";
}
Using it:
var account1 = new BankAccount("SB-1001", "Asha Rao", 5000);
var account2 = new BankAccount("SB-1002", "Ravi Kumar"); // opening balance defaults to 0
account1.Deposit(2500);
account2.Deposit(1000);
Console.WriteLine(account1);
Console.WriteLine(account2);
try
{
account2.Withdraw(5000);
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
Console.WriteLine($"Accounts opened: {BankAccount.TotalAccounts}");
_balanceis private:account1._balance = 1000000;does not compile. The only way to change money is through methods that enforce the rules.Balanceexposes the value read-only.AccountNumberandHolderhave onlyget, so they can be set in the constructor but never changed afterwards.TotalAccountsis static: it is read through the class name (BankAccount.TotalAccounts), not through an object, and counts every account ever created.- Overriding
ToString()controls whatConsole.WriteLine(account1)prints.
Classes are reference types
A variable of a class type doesn't hold the object itself, but a reference (address) to it. Copying the variable copies the reference, so both variables point to the same object:
BankAccount sameAccount = account1; // no new object is created
sameAccount.Deposit(500);
Console.WriteLine($"account1 balance after depositing through sameAccount: {account1.Balance}");
Console.WriteLine($"Same object? {ReferenceEquals(account1, sameAccount)}");
The deposit was made through sameAccount, but account1 shows it too, because it's one account with two names. This behaviour is important when you pass objects to methods; see Parameter Passing Techniques.
Class vs object
| Class | Object |
|---|---|
| A blueprint or template | A real instance built from the blueprint |
Declared once with the class keyword | Created many times with new |
| Describes which data and methods exist | Holds actual values (Asha's balance is 7500) |
| Takes no memory for instance data by itself | Each object takes memory on the heap |
BankAccount | account1, account2 |
Read more about objects in Object in C#.
Points to remember
- A class is a reference type; its objects live on the managed heap and are cleaned up by the garbage collector.
- A top-level class without an access modifier is internal by default (visible only inside the same project). Members without a modifier are private.
- Every class inherits from
System.Object, which is why every object hasToString(),Equals()andGetHashCode(). - If you don't write any constructor, C# adds a public parameterless one. Once you write your own, that default constructor is no longer generated.
- A class can inherit from only one class, but implement many interfaces.
- Special kinds of classes: abstract, sealed, static and partial.
Class vs struct (quick comparison)
| class | struct | |
|---|---|---|
| Type | Reference type | Value type |
| Assignment | Copies the reference (shared object) | Copies the whole value |
| Inheritance | Supported | Not supported (only interfaces) |
| Can be null | Yes | No (unless Nullable<T>) |
| Use for | Entities and services with behaviour: customers, orders, accounts | Small immutable values: points, money amounts, coordinates |
Interview questions
1. What is a class in C#?
A user-defined reference type that combines data (fields, properties) and behaviour (methods, events) into a single unit. Objects are created from it with new.
2. What is the default access modifier of a class?
internal for a top-level class. Members of a class are private by default.
3. What is the difference between a field and a property?
A field is a variable that stores data. A property wraps access to data with get/set accessors, so it can validate values, be read-only, or be computed, while looking like a field to the caller.
4. What is a static member?
A member that belongs to the class rather than to an object. All objects share it, and it is accessed through the class name, like BankAccount.TotalAccounts.
5. What happens when you assign one object variable to another?
Only the reference is copied; both variables refer to the same object, so a change through one is visible through the other.
Summary
- A class is a blueprint; objects are instances created with
new. - Classes contain fields, properties, constructors, methods and static members.
- Keep data private and change it only through methods that enforce the rules (encapsulation).
- Classes are reference types: assigning a variable shares the same object.
- Every class derives from
System.Object; top-level classes are internal by default.