Classes and Objects: Your First Student Class – C# | Lesson 37
Introduction
So far, a student has been loose variables: a name here, a mark there, a date of birth somewhere else. In Lesson 33, two separate arrays held names and marks, and one sort could mix them up.
A class fixes this. It describes what every student has and can do, in one place.
Each real student is then an object made from that class, holding all of its own data together. This is the start of object-oriented programming (OOP), the style used in almost every C# application.
By the end of this lesson, you will be able to:
- Explain the difference between a class and an object.
- Write a class with properties, a constructor, a computed property, and a method.
- Create objects with
new, and explain what a reference is. - Write your own exception class.
- Put shared classes in a class library project.
Why this matters
Every table in SchoolDb will become a C# class by Lesson 44. The Web API receives and returns objects, and Entity Framework Core reads rows straight into them.
Classes also decide where rules live. Lesson 29 calculated a student's age in SQL. In this
lesson, the same rule becomes a method of Student, so every part of the application asks
a student object for its age the same way.
The concept
Example: class and object
| Class | Object | |
|---|---|---|
| What it is | A design, written once | One real thing made from the design |
| SMS example | Student | Aarav, Diya, Kabir |
| How many | One per kind of thing | As many as you need |
| Created with | The class keyword | The new keyword |
Think of the school's admission form. The printed blank form is the class: it says every student has a name, a date of birth, and an admission number.
Each filled form is an object: Aarav's form, Diya's form. Same fields, different values.
Example: the four pillars of OOP
Object-oriented programming rests on four ideas. Module 7 teaches one per lesson, each on SMS code:
| Pillar | One-line meaning | Lesson |
|---|---|---|
| Encapsulation | A class protects its own data and rules | 38 |
| Inheritance | A class reuses and extends another class | 39 |
| Polymorphism | One call behaves differently for different types | 40 |
| Abstraction | Code depends on what something does, not how | 41 |
Interviewers ask about these four in almost every C# interview. Learning them on real school code makes the answers concrete instead of memorized.
Example: the parts of a class
| Part | Example in Student | Purpose |
|---|---|---|
| Property | public string FirstName { get; set; } | Data each object holds |
| Constructor | public Student(string admissionNumber, ...) | Runs once, when the object is created |
| Computed property | public string FullName => $"{FirstName} {LastName}"; | A value worked out from other properties |
| Method | public int GetAgeOn(DateOnly date) | Something the object can do |
public means code outside the class can use the member. Lesson 38 shows why some members
should not be public.
Example: creating an object
Student aarav = new Student("ADM-2017-0001", "Aarav", "Sharma", new DateOnly(2011, 4, 12));
new creates the object in memory and runs the constructor with these four arguments.
The variable aarav then refers to that object. Use a dot to reach its members:
aarav.FullName, aarav.GetAgeOn(...).
Example: variables hold references
A class is a reference type (Lesson 31). The variable does not hold the student itself; it holds a reference, like an address that says where the object lives.
Two variables can hold the same reference. Change the object through one, and the other sees the change, because there is only one object. This surprises many beginners, so the program in this lesson demonstrates it.
Apply it to the SMS
Step 1: a class library for shared code
Classes that every part of the SMS needs go in a separate project: a class library,
which compiles to a .dll that other projects use. The console app, the data layer, and
the Web API will all share it.
dotnet new classlib -n SchoolApp.Core -o src/SchoolApp.Core
dotnet sln SchoolApp.slnx add src/SchoolApp.Core/SchoolApp.Core.csproj
dotnet add src/SchoolApp.Console/SchoolApp.Console.csproj reference src/SchoolApp.Core/SchoolApp.Core.csproj
Delete the template's Class1.cs. The reference command lets the console app use
everything public in SchoolApp.Core.
The reference goes one way only: the console app knows about Core, but Core knows nothing about the console app. Core will never print to the screen or read the keyboard. That keeps it usable from a Web API, a test project, or a background job, which have no console at all.
Step 2: the Student class
namespace SchoolApp.Core.Entities;
// Lesson 37: a class describes what every student has (properties) and can do (methods).
public class Student
{
public int Id { get; set; }
public string AdmissionNumber { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public DateOnly DateOfBirth { get; set; }
// The constructor runs when an object is created with new.
public Student(string admissionNumber, string firstName, string lastName, DateOnly dateOfBirth)
{
AdmissionNumber = admissionNumber;
FirstName = firstName;
LastName = lastName;
DateOfBirth = dateOfBirth;
}
// A computed property: worked out from other properties each time it is read.
public string FullName => $"{FirstName} {LastName}";
// A method: the age on a given date, the same rule as Lesson 29's SQL task.
public int GetAgeOn(DateOnly date)
{
int age = date.Year - DateOfBirth.Year;
if (DateOfBirth > date.AddYears(-age))
{
age--; // the birthday has not happened yet in that year
}
return age;
}
}
The first line, namespace SchoolApp.Core.Entities;, puts the class in a namespace: a
named group of related types, matching the folder. Other files write
using SchoolApp.Core.Entities; to use it.
The constructor has the same name as the class and no return type. It makes sure every
Student is created with all four required values, so no student can exist without a name.
Step 3: your own exception
Lesson 36 promised this. A custom exception is a class that inherits from Exception;
the : Exception part says so, and Lesson 39 explains inheritance.
namespace SchoolApp.Core.Exceptions;
// Lesson 37: our own exception type for broken school rules.
// ErrorNumber uses the same numbers as the SQL code, such as 50001 for impossible marks.
public class SchoolRuleException : Exception
{
public int ErrorNumber { get; }
public SchoolRuleException(int errorNumber, string message) : base(message)
{
ErrorNumber = errorNumber;
}
}
: base(message) passes the message on to Exception, so ex.Message works as usual.
ErrorNumber adds what Exception lacks: the same error numbers that the SQL code uses,
such as 50001.
Step 4: objects in the program
Show src/SchoolApp.Console/Program.cs
// Lesson 37: classes and objects. Student lives in SchoolApp.Core; ExamResult is at the end of this file.
using SchoolApp.Core.Entities;
using SchoolApp.Core.Exceptions;
// 1. Create objects with new: each object is one separate student.
Student aarav = new Student("ADM-2017-0001", "Aarav", "Sharma", new DateOnly(2011, 4, 12));
Student diya = new Student("ADM-2017-0002", "Diya", "Patel", new DateOnly(2011, 8, 23));
Console.WriteLine($"{aarav.FullName} ({aarav.AdmissionNumber})");
Console.WriteLine($"{diya.FullName} is {diya.GetAgeOn(new DateOnly(2026, 7, 1))} on 2026-07-01");
// 2. One array of objects replaces two parallel arrays: each name stays with its mark.
ExamResult[] mathematics =
{
new ExamResult(aarav, 92m, 100m),
new ExamResult(diya, 85m, 100m),
new ExamResult(new Student("ADM-2017-0003", "Kabir", "Singh", new DateOnly(2011, 1, 30)), 67m, 100m),
new ExamResult(new Student("ADM-2017-0004", "Meera", "Iyer", new DateOnly(2011, 11, 5)), 95m, 100m),
new ExamResult(new Student("ADM-2017-0005", "Rohan", "Das", new DateOnly(2011, 6, 17)), 58m, 100m),
new ExamResult(new Student("ADM-2017-0006", "Sana", "Sheikh", new DateOnly(2011, 3, 9)), 78m, 100m),
};
ExamResult topper = mathematics[0];
foreach (ExamResult result in mathematics)
{
if (result.Marks > topper.Marks)
{
topper = result;
}
}
Console.WriteLine($"Topper: {topper.Student.FullName}, {topper.Percentage}%");
// 3. Variables hold references: two variables can point to the same object.
Student sameStudent = aarav;
sameStudent.FirstName = "Aaru";
Console.WriteLine($"After changing sameStudent: aarav.FirstName is {aarav.FirstName}");
aarav.FirstName = "Aarav";
// 4. Our own exception: the constructor refuses an impossible mark.
try
{
ExamResult wrong = new ExamResult(aarav, 105m, 100m);
}
catch (SchoolRuleException ex)
{
Console.WriteLine($"Error {ex.ErrorNumber}: {ex.Message}");
}
// One student's result in one paper. Its constructor makes sure every result is valid.
class ExamResult
{
public Student Student { get; }
public decimal Marks { get; }
public decimal MaxMarks { get; }
public ExamResult(Student student, decimal marks, decimal maxMarks)
{
if (marks < 0 || marks > maxMarks)
{
throw new SchoolRuleException(50001, $"Marks must be between 0 and {maxMarks}; got {marks}.");
}
Student = student;
Marks = marks;
MaxMarks = maxMarks;
}
public decimal Percentage => Math.Round(Marks / MaxMarks * 100, 2);
}
Aarav Sharma (ADM-2017-0001)
Diya Patel is 14 on 2026-07-01
Topper: Meera Iyer, 95.00%
After changing sameStudent: aarav.FirstName is Aaru
Error 50001: Marks must be between 0 and 100; got 105.
Example: the age rule as a method
diya.GetAgeOn(new DateOnly(2026, 7, 1)) returns 14: Diya's birthday is on 23 August,
so she has not turned 15 yet on 1 July. It is the same answer Lesson 29 got in SQL.
The method uses the object's own DateOfBirth; you do not pass it in. That is the point
of a method: the object already knows its own data.
Example: an array of objects
ExamResult[] mathematics holds six objects. Each one keeps a student and a mark
together, so the topper loop returns Meera with her own mark, 95.
Compare Lesson 33, where names[2] and marks[2] only matched if nobody reordered one of
the arrays. With objects, a mark can never drift away from its student.
Example: a shared reference
Student sameStudent = aarav; copies the reference, not the student. Setting
sameStudent.FirstName = "Aaru" changed the only Aarav object, so aarav.FirstName printed
Aaru. The program then sets it back.
In the Web API, the same object is often passed through several methods. Any of them can change it, and every holder of the reference sees the change.
Example: printing and comparing objects
Two surprises wait for beginners. Console.WriteLine(aarav) does not print the student's
name. It prints the class's full name:
SchoolApp.Core.Entities.Student
And two separate Student objects with exactly the same values are not equal with
==. It printed False, because == on a class compares references: are these the same
object? Lesson 40 shows how to choose what printing shows, and Lesson 43 shows records,
which compare by value.
A variable can also hold no reference at all: Student? nobody = null;. Using
nobody.FullName throws the NullReferenceException from Lesson 35, for the same reason:
there is no object to ask.
Example: a constructor that guards the rules
ExamResult's constructor refuses a mark of 105 out of 100 by throwing
SchoolRuleException with error 50001. An invalid ExamResult can never exist, because
creating one fails.
ExamResult is written at the end of Program.cs, after the top-level statements. A class
used by one file can live there; classes the whole SMS needs belong in SchoolApp.Core.
Common mistakes
- Calling
new Student()with no arguments. The class has no such constructor:error CS7036: There is no argument given that corresponds to the required parameter 'admissionNumber'. Fix: pass every required value. - Calling a method on the class instead of an object.
Student.GetAgeOn(...)fails witherror CS0120: An object reference is required for the non-static field, method, or property. Fix: call it on an object, such asdiya.GetAgeOn(...). - Expecting
=to copy an object. It copies the reference. Fix: create a new object when you need a separate copy. - Using
Exceptionfor every rule. Callers cannot catch your rule separately. Fix: throw a specific type such asSchoolRuleException. - Putting shared classes in the console project. The Web API cannot use them. Fix:
shared classes go in
SchoolApp.Core.
In real projects
The first school system I maintained had no classes for its data. Each screen passed around separate variables, and one report showed one student's name with another student's marks after a sorting change.
Moving to a Student class and a Result class removed that whole category of bug. It
also made the code shorter, because the rules moved into methods next to the data.
My rule now: if three values always travel together, they belong in one class. And if a rule uses those values, it belongs in that class too.
Best practices
- Name classes as singular nouns in PascalCase:
Student, notStudentsorStudentData. - Use constructors to require the values an object cannot exist without.
- Put behaviour that uses an object's data in a method of that class.
- Keep shared classes in a class library with namespaces that match folders.
- Create specific exception types for your application's rules.
Recap
- A class is a design; an object is one thing made from it with
new. - Properties hold data, constructors set it up, and methods add behaviour.
- A computed property, such as
FullName, is calculated each time it is read. - Variables hold references; two variables can point to one object.
- A custom exception inherits from
Exceptionand can carry extra data, such asErrorNumber. SchoolApp.Coreis the class library that every part of the SMS will share.
Homework
Library module task: In a single-file app, write a Book class (ISBN, title, author)
and a BookIssue class. A BookIssue holds a book, a student's name, and the issue date.
Give BookIssue a computed DueOn property, 14 days after the issue date, and a
CalculateFine method that charges 2 per day after the due date.
A correct result looks like:
The Jungle Book by Rudyard Kipling
Issued to Advika Sharma on 2026-07-06, due 2026-07-20
Fine if returned 2026-07-28: 16
Show the solution
// Library homework, Lesson 37: Book and BookIssue as classes.
// Run with: dotnet run library-assignment/037-library-classes.cs
Book jungleBook = new Book("9780141325293", "The Jungle Book", "Rudyard Kipling");
BookIssue issue = new BookIssue(jungleBook, "Advika Sharma", new DateOnly(2026, 7, 6));
Console.WriteLine($"{issue.Book.Title} by {issue.Book.Author}");
Console.WriteLine($"Issued to {issue.StudentName} on {issue.IssuedOn:yyyy-MM-dd}, due {issue.DueOn:yyyy-MM-dd}");
Console.WriteLine($"Fine if returned 2026-07-28: {issue.CalculateFine(new DateOnly(2026, 7, 28))}");
class Book
{
public string Isbn { get; }
public string Title { get; }
public string Author { get; }
public Book(string isbn, string title, string author)
{
Isbn = isbn;
Title = title;
Author = author;
}
}
class BookIssue
{
public Book Book { get; }
public string StudentName { get; }
public DateOnly IssuedOn { get; }
public DateOnly DueOn => IssuedOn.AddDays(14);
public BookIssue(Book book, string studentName, DateOnly issuedOn)
{
Book = book;
StudentName = studentName;
IssuedOn = issuedOn;
}
public decimal CalculateFine(DateOnly returnedOn)
{
int daysLate = returnedOn.DayNumber - DueOn.DayNumber;
return daysLate > 0 ? daysLate * 2m : 0m;
}
}
DueOn is computed from IssuedOn, so it can never disagree with it. Compare Lesson 31,
where the due date was a separate variable you had to keep correct yourself.
Quiz
What is the difference between a class and an object?
Show answer
Answer: B. A class is a design; an object is one thing made from it Student is the class; Aarav and Diya are objects.
When does a constructor run?
Show answer
Answer: B. When the object is created with new new creates the object and runs the constructor once.
b = a; b.FirstName = 'Aaru'; for a Student object a. What is a.FirstName?
Show answer
Answer: B. Aaru a and b refer to the same object.
What does : base(message) do in SchoolRuleException's constructor?
Show answer
Answer: B. Passes the message to the Exception constructor The base class Exception stores the message, so ex.Message works.
Why does Student live in SchoolApp.Core instead of the console project?
Show answer
Answer: B. So the console app, data layer, and Web API can all share it A class library is shared code used by several projects.
Interview questions
Questions are grouped by experience level. Click a question to see a model answer. All questions: OOP interview questions.
Fresher (0-2 yrs)
F What is the difference between a class and an object? concept
A class is a design that describes what something has (properties) and can do (methods).
An object is one instance created from it with new. Student is a class; Aarav and Diya
are objects with their own values.
F What is a constructor? concept
A special method with the class's name and no return type that runs once when an object is created. It sets up the object, and by requiring parameters it ensures no object exists without the values it needs, such as a student's name.
F What are the four pillars of object-oriented programming? concept
Encapsulation (a class protects its data and rules), inheritance (a class reuses and extends another), polymorphism (one call behaves differently for different types), and abstraction (code depends on what something does, not how).
Mid (3-5 yrs)
M Two variables refer to one object, and you change a property through one of them. What does the other see? concept
The change. Classes are reference types: assignment copies the reference, not the object, so both variables point to the same object. To get an independent copy, create a new object.
M Why does == return false for two Student objects with identical values? concept
For classes, == compares references by default: it asks whether both variables point to the
same object. Two separately created objects are different objects. Records, or overriding
Equals, give value-based equality.
M How do you create a custom exception, and why would you? coding
Create a class that inherits from Exception and passes the message to base(message), adding
any extra data, such as an ErrorNumber. Callers can then catch your application's rule
failures separately from system errors and react to them specifically.
Senior (6-9 yrs)
S A report shows one student's name with another student's marks. The code uses two arrays. How would you fix it? scenario
The parallel arrays went out of step, probably after sorting one of them. Replace them with one
class that holds the student and the mark together, such as ExamResult, and keep a single
collection of those objects.
How to answer: Explain the root cause, the object-based design, and why it removes the whole class of bug. Mention testing the report after sorting and filtering.
S What is a 'god class', and how do you avoid one? design
A class that knows and does too much, such as a SchoolManager with hundreds of methods for
students, fees, and exams. It is hard to change and test. Split it by responsibility into
smaller classes, each with its own data and the rules that use that data.
How to answer: Give the warning signs (huge files, unrelated methods, many reasons to change), the refactoring approach, and connect it to the Single Responsibility Principle (Lesson 46).
Architect / Lead (10+ yrs)
A How would you decide which classes go into a shared core library? design
Put the domain model and business rules that every application needs (entities, rule exceptions, interfaces) in the core library, with no dependency on UI, databases, or web frameworks. Keep presentation and infrastructure code in the projects that need them, which reference the core, never the other way round.
How to answer: Explain dependency direction and why it matters for reuse and testing. Mention keeping the core
free of Console, HTTP, and SQL so it works from an API, tests, or background jobs.
Resources
- Code for this lesson: git tag
lesson-37-classes-objects(code repository coming soon) - Files:
src/SchoolApp.Core/Entities/Student.cs,src/SchoolApp.Core/Exceptions/SchoolRuleException.cs,src/SchoolApp.Console/Program.cs,library-assignment/037-library-classes.cs - Video: coming soon
What's next
Right now, any code can set any property, including an invoice's status to "Paid" without a payment. Next: encapsulation, where a class protects its own data. Lesson 38: Encapsulation
Want live sessions, code reviews, and interview preparation? Join the next batch at Sahasra Technologies.