Skip to main content

Classes and Objects: Your First Student Class – C# | Lesson 37

Published / updated

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​

ClassObject
What it isA design, written onceOne real thing made from the design
SMS exampleStudentAarav, Diya, Kabir
How manyOne per kind of thingAs many as you need
Created withThe class keywordThe 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:

PillarOne-line meaningLesson
EncapsulationA class protects its own data and rules38
InheritanceA class reuses and extends another class39
PolymorphismOne call behaves differently for different types40
AbstractionCode depends on what something does, not how41

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​

PartExample in StudentPurpose
Propertypublic string FirstName { get; set; }Data each object holds
Constructorpublic Student(string admissionNumber, ...)Runs once, when the object is created
Computed propertypublic string FullName => $"{FirstName} {LastName}";A value worked out from other properties
Methodpublic 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​

Example
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.

Terminal
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​

src/SchoolApp.Core/Entities/Student.cs
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.

src/SchoolApp.Core/Exceptions/SchoolRuleException.cs
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
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);
}
Output
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:

Output
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​

  1. 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.
  2. Calling a method on the class instead of an object. Student.GetAgeOn(...) fails with error CS0120: An object reference is required for the non-static field, method, or property. Fix: call it on an object, such as diya.GetAgeOn(...).
  3. Expecting = to copy an object. It copies the reference. Fix: create a new object when you need a separate copy.
  4. Using Exception for every rule. Callers cannot catch your rule separately. Fix: throw a specific type such as SchoolRuleException.
  5. Putting shared classes in the console project. The Web API cannot use them. Fix: shared classes go in SchoolApp.Core.

In real projects​

From 16 years of .NET 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, not Students or StudentData.
  • 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 Exception and can carry extra data, such as ErrorNumber.
  • SchoolApp.Core is 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:

Output
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-assignment/037-library-classes.cs
// 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​

  1. What is the difference between a class and an object?

    1. They are the same
    2. A class is a design; an object is one thing made from it
    3. An object is a design; a class is made from it
    4. A class holds data; an object holds methods
    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.

  2. When does a constructor run?

    1. Every time a property is read
    2. When the object is created with new
    3. When the program ends
    4. Only when called by name
    Show answer

    Answer: B. When the object is created with new new creates the object and runs the constructor once.

  3. b = a; b.FirstName = 'Aaru'; for a Student object a. What is a.FirstName?

    1. The old name
    2. Aaru
    3. null
    4. An error
    Show answer

    Answer: B. Aaru a and b refer to the same object.

  4. What does : base(message) do in SchoolRuleException's constructor?

    1. Creates a second exception
    2. Passes the message to the Exception constructor
    3. Throws the exception
    4. Logs the message
    Show answer

    Answer: B. Passes the message to the Exception constructor The base class Exception stores the message, so ex.Message works.

  5. Why does Student live in SchoolApp.Core instead of the console project?

    1. Console projects cannot have classes
    2. So the console app, data layer, and Web API can all share it
    3. Class libraries run faster
    4. It is required by C#
    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.

See the example · Permalink

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.

See the example · Permalink

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).

See the example · Permalink

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.

See the example · Permalink

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.

See the example · Permalink

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.

See the example · Permalink

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.

See the example · Permalink

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).

See the example · Permalink

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.

See the example · Permalink

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.