C# Classes Objects
Classes and objects are the foundation of object-oriented C# programming. A class describes a new type: what data it stores and what actions it can perform. An object is a live instance of that class, created while the program runs. Understanding this difference is essential because most useful C# code is built by designing types and then creating objects from them.
Overview: How Classes and Objects Work
A class is a blueprint for a reference type. It can contain fields, properties, constructors, methods, events, nested types, and other members. For beginners, the most important members are fields for internal state, properties for controlled access, constructors for initialization, and methods for behavior. A class declaration by itself does not create data. It only teaches the compiler and runtime what an object of that type should look like.
An object is an instance of a class. When code runs new Customer(...), the Common Language Runtime allocates memory for a Customer object, initializes its fields, runs the matching constructor, and returns a reference to the new object. A variable of a class type usually stores that reference, not the whole object. This is why two variables can refer to the same object, and a change through one variable can be observed through the other.
Class instances are normally allocated on the managed heap. The CLR manages their lifetime with garbage collection: when no reachable references point to an object anymore, the object becomes eligible to be reclaimed later. You do not usually free class objects manually in C#. However, if an object owns external resources such as files or network connections, it may implement IDisposable; that is a separate resource-cleanup concern from ordinary memory management.
Inside an instance method, C# provides a hidden reference named this. It means “the current object.” If a method reads Name or assigns age, it is really reading or changing the state of the object on which the method was called. That is how two objects created from the same class can share the same code but keep different data.
Syntax
class ClassName
{
private string fieldName;
public ClassName(string value)
{
fieldName = value;
}
public string PropertyName => fieldName;
public void MethodName()
{
Console.WriteLine(fieldName);
}
}
| Part | Meaning |
|---|---|
class ClassName |
Declares a new class type. Class names usually use PascalCase and describe a noun or concept. |
private string fieldName |
Declares a field, which stores data inside each object. Private fields protect internal state. |
public ClassName(...) |
Declares a constructor. It runs when new creates an object. |
public string PropertyName |
Declares a property, commonly used to expose object data in a controlled way. |
public void MethodName() |
Declares behavior that can be called on an object. |
Examples
Example 1: Create Two Objects From One Class
using System;
class Student
{
private string topic;
public Student(string name, int age, string topic)
{
Name = name;
Age = age;
this.topic = topic;
}
public string Name { get; }
public int Age { get; }
public void Introduce()
{
Console.WriteLine($"{Name} ({Age}) is learning {topic}.");
}
}
class Program
{
static void Main()
{
Student first = new Student("Maya", 29, "C#");
Student second = new Student("Jon", 34, "databases");
first.Introduce();
second.Introduce();
}
}
Output:
Maya (29) is learning C#.
Jon (34) is learning databases.
The Student class is written once, but the program creates two separate objects. Each object has its own Name, Age, and private topic value. Calling Introduce on first uses Maya’s data, while calling it on second uses Jon’s data.
Example 2: Protect Object State With Properties and Methods
using System;
class InventoryItem
{
private int quantity;
public InventoryItem(string name, int startingQuantity)
{
Name = name;
AddStock(startingQuantity);
}
public string Name { get; }
public int Quantity => quantity;
public void AddStock(int amount)
{
if (amount < 0)
{
throw new ArgumentOutOfRangeException(nameof(amount), "Amount cannot be negative.");
}
quantity += amount;
}
public bool TrySell(int amount)
{
if (amount <= 0 || amount > quantity)
{
return false;
}
quantity -= amount;
return true;
}
}
class Program
{
static void Main()
{
InventoryItem keyboard = new InventoryItem("Keyboard", 10);
keyboard.AddStock(5);
bool sold = keyboard.TrySell(3);
Console.WriteLine(keyboard.Name);
Console.WriteLine(keyboard.Quantity);
Console.WriteLine(sold);
}
}
Output:
Keyboard
12
True
This class keeps quantity private, so outside code cannot set it to an invalid value directly. The public methods are the safe ways to change the object. This is a core class design habit: keep the rules next to the data they protect.
Example 3: Class Variables Hold References
using System;
class Counter
{
public int Value { get; private set; }
public void Increment()
{
Value++;
}
}
class Program
{
static void Main()
{
Counter first = new Counter();
Counter second = first;
first.Increment();
second.Increment();
Console.WriteLine(first.Value);
Console.WriteLine(object.ReferenceEquals(first, second));
}
}
Output:
2
True
The assignment Counter second = first; copies the reference, not the object. Both variables point to the same Counter instance, so incrementing through either variable changes one shared object. ReferenceEquals confirms that both variables refer to the same object identity.
How It Works Step by Step
- The compiler reads the class declaration and records the class name, members, accessibility, constructor signatures, and type information.
- When the program reaches
new Student("Maya", 29, "C#"), the CLR allocates enough heap memory for oneStudentobject and its instance fields. - Fields first receive default values: references become
null, numbers become0, andboolvalues becomefalse. - The selected constructor runs. Constructor parameters are ordinary local values; assignments such as
Name = namecopy those values into the object’s state. - The
newexpression returns a reference. The variable stores that reference, allowing later code to find the object. - When code calls an instance method, the current object is passed as
this. The method body can read and change that object’s fields and properties. - When no live references can reach the object anymore, the garbage collector may reclaim its memory during a future collection.
Common Mistakes
Trying to Call an Instance Method on the Class
class Person
{
public void SayHello()
{
Console.WriteLine("Hello");
}
}
Person.SayHello();
This is wrong because SayHello belongs to an object, not to the class itself. Unless a method is declared static, create an instance first.
using System;
class Person
{
public void SayHello()
{
Console.WriteLine("Hello");
}
}
class Program
{
static void Main()
{
Person person = new Person();
person.SayHello();
}
}
Output:
Hello
Expecting Assignment to Clone an Object
using System;
class Box
{
public string Label { get; set; } = "Original";
}
class Program
{
static void Main()
{
Box a = new Box();
Box b = a;
b.Label = "Changed";
Console.WriteLine(a.Label);
}
}
Output:
Changed
This compiles, but it surprises many learners. a and b refer to the same object. If you need a separate object, create a new instance and copy the values you want.
using System;
class Box
{
public Box(string label)
{
Label = label;
}
public string Label { get; set; }
}
class Program
{
static void Main()
{
Box a = new Box("Original");
Box b = new Box(a.Label);
b.Label = "Changed";
Console.WriteLine(a.Label);
Console.WriteLine(b.Label);
}
}
Output:
Original
Changed
Leaving Objects Half Initialized
Customer customer = new Customer();
customer.Name = "Ava";
customer.Email = "ava@example.com";
Object initializers are useful, but for required data a constructor is often clearer because it prevents the object from existing without its essential values. A constructor should leave the object ready to use immediately.
Best Practices
- Give each class one clear responsibility. A class named
Invoiceshould not also send email, read files, and manage application settings. - Prefer private fields with public properties or methods instead of public mutable fields.
- Use constructors for required values so objects cannot be created in an invalid state.
- Use
readonlyfields or get-only properties for values that should not change after construction. - Name classes with nouns such as
Order,Customer, andReportPrinter. Name methods with verbs such asCalculateTotalorPrint. - Remember reference behavior. Assignment, parameter passing, and returns usually copy the reference to a class object, not the object itself.
- Keep validation inside the class when the validation protects that class’s own state.
- Do not make everything
static. Use objects when each instance needs its own data.
Practice Exercises
- Create a
Bookclass with requiredTitleandAuthorvalues. Add a method that printsTitle by Author. - Create a
Thermostatclass that stores Celsius privately. Add methods to raise and lower the temperature, and a read-only Fahrenheit property. - Create a
ShoppingCartItemclass with a product name, unit price, quantity, and a method or property that returns the line total.
Summary
- A class defines a type; an object is an instance created from that type.
newallocates an object, initializes fields, runs a constructor, and returns a reference.- Each object has its own instance state, even though objects of the same class share the same member definitions.
- Class variables usually hold references, so assignment does not automatically clone objects.
- Fields store state, properties expose data, methods define behavior, and constructors put objects into a valid starting state.
- Good class design protects state, makes invalid objects hard to create, and keeps responsibilities focused.
