Unit 1: C# Basics [8 Hours]
C# Basics introduces the core structure used to build programs on the .NET platform. The unit begins with .NET architecture and object-oriented building blocks such as classes, objects, interfaces, access modifiers, arrays, and inheritance. It then develops program reliability and concurrent execution through exception handling and multithreading. The final part explains file input/output, text and binary streams, and serialization for storing data permanently.
Tested Topics
- File I/O — 4x (2026, 2025, 2023, 2021)
- Multithreading — 3x (2025, 2023, 2021)
- Binary file reading and writing — 2x (2023, 2021)
- Interfaces — 2x (2025, 2021)
- .NET Architecture — 1x (2021)
- Class vs Interface — 1x (2025)
- Exception handling: throw and throws — 1x (2021)
- File handling classes — 1x (2026)
- Interfaces and multiple inheritance — 1x (2021)
- Serialization — 1x (2025)
- Text files vs binary files — 1x (2026)
- Thread lifecycle — 1x (2023)
- Thread Sleep() method — 1x (2021)
- Variable-size and multidimensional arrays — 1x (2026)
1.1 Introduction to .NET Architecture
Definition
.NET architecture is the execution environment and collection of services used to compile, run, manage, and support applications written in C# and other .NET-compatible programming languages.
A C# program does not normally go directly from source code to processor instructions. It first becomes an intermediate form that .NET understands. The .NET runtime then loads that program, converts the required parts into machine instructions, manages memory, handles exceptions, and provides standard libraries.
Detailed Explanation
The architecture of .NET separates a program into several layers. A developer writes source code, the compiler translates the code into an intermediate language, and a runtime environment manages execution. This design allows different .NET languages to share the same execution system and libraries.
Main Components of .NET Architecture
1. Common Language Runtime
The Common Language Runtime (CLR) is the execution engine of .NET. It loads and executes managed .NET programs.
The CLR provides services such as:
- Memory management: allocates and releases memory required by objects.
- Garbage collection: removes objects that are no longer reachable by the program.
- Exception handling: provides a common mechanism for detecting and handling runtime errors.
- Thread management: provides facilities for executing multiple threads.
- Type safety: checks that values and objects are used according to compatible types.
- Security services: works with runtime and operating-system security mechanisms.
- Code execution: converts intermediate code into native machine code when required.
2. Common Intermediate Language
When C# source code is compiled, it is normally converted into Common Intermediate Language (CIL). Older Microsoft documentation frequently uses the name Microsoft Intermediate Language (MSIL) for essentially the same intermediate instruction format.
CIL is processor-independent. This means the compiled assembly does not initially contain only the final machine instructions of a particular Central Processing Unit (CPU).
3. Just-In-Time Compiler
The Just-In-Time (JIT) compiler converts intermediate language instructions into native machine instructions that the current processor can execute.
The general execution sequence is:
C# source code → C# compiler → CIL and metadata → assembly → CLR loads assembly → JIT compilation → native machine instructions → execution
4. Common Type System
The Common Type System (CTS) defines how data types are declared, represented, and used by .NET languages.
For example, the C# keyword int represents the .NET type System.Int32. Because .NET languages share the CTS, values can be exchanged between languages using a common type model.
5. Common Language Specification
The Common Language Specification (CLS) is a set of rules that language designers can follow so that components written in one .NET language can be used conveniently from another .NET language.
The CTS defines the complete type system, while the CLS defines a common interoperable subset of rules.
6. Base Class Library
The Base Class Library (BCL) provides reusable classes and types for common programming operations.
Examples include:
- System.Console for console input and output.
- System.String for text.
- System.Math for mathematical operations.
- System.IO for file and stream operations.
- System.Threading for thread-related operations.
- System.Collections and related namespaces for collection types.
The broader collection of libraries available to .NET applications is often described as the Framework Class Library (FCL), especially in traditional .NET Framework terminology.
7. Assemblies
An assembly is a compiled unit of a .NET application. It commonly appears as an executable file or library file and contains intermediate code, metadata, and information required by the runtime.
Typical extensions are:
- .exe: executable application.
- .dll: Dynamic Link Library containing reusable compiled code.
8. Metadata
Metadata describes the types contained in an assembly. It records information such as classes, methods, properties, fields, and references to other assemblies.
Because type information travels with the compiled assembly, the CLR can inspect and manage types at runtime.
9. Garbage Collector
The Garbage Collector (GC) automatically reclaims managed memory occupied by objects that can no longer be reached by the application.
This reduces the amount of manual memory management required from C# programmers.
Managed and Unmanaged Code
| Basis | Managed Code | Unmanaged Code |
|---|---|---|
| Execution | Runs under management of the CLR | Runs directly as native code outside normal CLR management |
| Memory | Managed memory can be handled by the garbage collector | Memory/resource handling depends on native program rules |
| Type checking | CLR provides runtime type services | Depends on the native environment and language |
| Examples | Normal C# application code | Native libraries written in languages such as C or C++ |
Let's Clarify
Consider a C# program as a document written in a language understood by the .NET system rather than directly by the processor. The C# compiler first prepares a common intermediate version. When the program reaches a particular computer, the JIT compiler translates the needed instructions into the machine language of that computer. Meanwhile, the CLR supervises execution, memory, exceptions, and threads.
Common Mistake
The CLR and .NET are not identical terms. .NET is the wider platform containing the runtime, libraries, development tools, and application technologies. The CLR is the runtime component responsible for executing managed code.
Summary
- .NET architecture provides the environment in which C# applications are compiled, executed, and managed.
- The CLR manages execution, memory, exceptions, types, and threads.
- C# code is normally compiled into CIL before execution as native machine code.
- The JIT compiler converts required intermediate instructions into processor-specific instructions.
- CTS standardizes .NET types, while CLS provides interoperability rules for .NET languages.
- The BCL supplies reusable classes for common programming tasks.
Test Yourself
- Describe the execution of a C# program from source code to native execution.
- Differentiate between CTS and CLS.
- Explain Net architecture in detail. (2021)
Hints:
- C# source → compiler → CIL and metadata → assembly → CLR → JIT → native instructions.
- CTS defines the complete .NET type system. CLS defines interoperability rules that languages can follow.
- Describe CLR, CIL, JIT, CTS, CLS, BCL/FCL, assemblies, metadata, garbage collection, and the execution flow.
1.2 Class and Object
Definition
A class is a programmer-defined type that specifies the data and behavior that its objects can contain. An object is an actual instance of a class created during program execution.
A class describes what an item should contain and what it should be able to do. An object is one actual item created from that description. A class called Student may define name, roll number, and a Display() method, while Ram and Sita can be two separate Student objects.
Detailed Explanation
C# is primarily an object-oriented programming language. In object-oriented programming, related data and the operations performed on that data are grouped inside classes.
A class can contain several members:
- Fields: variables that store object data.
- Properties: controlled members used to read or write values.
- Methods: operations that an object or class can perform.
- Constructors: special members used when creating objects.
- Events: notification mechanisms used in event-driven programming.
- Nested types: types declared within another type.
Class Example
using System;
class Student
{
// Fields store data belonging to each Student object.
public int Roll;
public string Name;
// Method defines behavior of Student objects.
public void Display()
{
Console.WriteLine("Roll: " + Roll);
Console.WriteLine("Name: " + Name);
}
}
class Program
{
static void Main()
{
// Create the first object of Student.
Student s1 = new Student();
s1.Roll = 101;
s1.Name = "Anita";
// Create another independent Student object.
Student s2 = new Student();
s2.Roll = 102;
s2.Name = "Rohan";
// Each object uses the method defined by the class.
s1.Display();
s2.Display();
}
}Although s1 and s2 are created from the same class, each object has its own instance data.
Class and Object Compared
| Basis | Class | Object |
|---|---|---|
| Meaning | Definition of a type | Instance of that type |
| Existence | Declared in source code | Created while the program runs |
| Role | Specifies members and behavior | Stores actual values and uses behavior |
| Example | Student | s1, s2 |
| Creation syntax | class Student { } | Student s = new Student(); |
Reference Variables
Most class objects in C# are accessed through references. A statement such as:
Student s = new Student();contains two major parts:
- Student s: declares a variable capable of referring to a Student object.
- new Student(): creates a new Student object and runs its constructor.
Summary
- A class defines the structure and behavior of objects.
- An object is an individual instance created from a class.
- Fields and properties hold data, while methods define behavior.
- Multiple objects of the same class can store different values.
- The new operator creates an object and invokes its constructor.
Test Yourself
- Define class and object with a suitable C# example.
- Explain the relationship between a class, an object, and an object reference.
Hints:
- Class = programmer-defined type; object = instance. Example: Student class and s1 object.
- Class defines members, object stores actual instance values, and a reference variable points to the object.
1.3 Creating Classes and Objects
Definition
Creating a class means declaring a new C# type with its required members, while creating an object means allocating an instance of that class, normally with the new operator.
First the program describes what a Student contains. Then it creates one or more actual Student objects and gives each object its own values.
Detailed Explanation
Basic Class Declaration
class ClassName
{
// Fields, properties, constructors and methods go here.
}The keyword class begins a class declaration. The class name identifies the new type.
Creating an Object
ClassName objectName = new ClassName();The creation process can be understood in sequence:
Declare reference variable → use new → allocate object → execute constructor → return object reference → store reference in variable
Constructors
A constructor is a special class member that has the same name as the class and is executed when an object is created.
A constructor has no return type, not even void.
using System;
class Student
{
private int roll;
private string name;
// Constructor receives initial values for a new object.
public Student(int r, string n)
{
roll = r;
name = n;
}
public void Display()
{
Console.WriteLine("Roll: " + roll);
Console.WriteLine("Name: " + name);
}
}
class Program
{
static void Main()
{
// The constructor is called when each object is created.
Student s1 = new Student(1, "Asha");
Student s2 = new Student(2, "Bikash");
s1.Display();
s2.Display();
}
}Default Constructor
If a class declares no instance constructor, C# provides a parameterless constructor for a normal class automatically. Once a programmer declares a constructor, the compiler does not automatically add another parameterless instance constructor.
Object Initialization
Properties can also be assigned when an object is created through an object initializer.
using System;
class Product
{
public int Id { get; set; }
public string Name { get; set; }
}
class Program
{
static void Main()
{
// Create an object and initialize its properties.
Product p = new Product
{
Id = 10,
Name = "Keyboard"
};
Console.WriteLine(p.Name);
}
}Common Mistake
A constructor is not an ordinary method. It does not have a return type and is invoked as part of object creation. Writing void Student() creates a method named Student rather than a constructor.
Summary
- A class is declared with the class keyword and contains related members.
- An object is normally created with the new operator.
- A constructor initializes a newly created object.
- A constructor has the same name as its class and has no return type.
- Each object receives its own instance data unless a member is declared static.
Test Yourself
- Write the general syntax for declaring a class and creating its object.
- What is a constructor? Explain its role during object creation.
Hints:
class A { }declares a class;A obj = new A();creates an object.- A constructor runs when an object is created and establishes its initial state.
1.4 Interface
Definition
An interface in C# defines a contract that specifies members a class or structure must provide when it implements that interface.
An interface tells a class what capabilities it must provide without requiring the class to inherit those capabilities from a common parent class. A class can follow several interface contracts at the same time.
Detailed Explanation
An interface is declared using the interface keyword. A class implements an interface by placing the interface name after a colon in the class declaration.
interface IPrintable
{
void Print();
}
class Report : IPrintable
{
public void Print()
{
Console.WriteLine("Printing report.");
}
}The class Report promises to provide the members required by IPrintable.
Main Uses of Interfaces
Interfaces have several practical uses. Two central uses are:
- Defining a common contract: unrelated classes can promise to provide the same operations.
- Supporting multiple interface inheritance: one class can implement several interfaces, allowing it to combine multiple contracts even though C# does not allow inheritance from multiple classes.
Interfaces also support loose coupling. Code can depend on a capability represented by an interface rather than depending directly on one specific implementation class.
Interface Example
using System;
interface IPrintable
{
void Print();
}
class StudentReport : IPrintable
{
public void Print()
{
Console.WriteLine("Student report printed.");
}
}
class Program
{
static void Main()
{
// Interface reference can refer to an implementing object.
IPrintable item = new StudentReport();
item.Print();
}
}Multiple Inheritance Through Interfaces
C# does not allow a class to inherit implementation from two different base classes. A class can, however, implement multiple interfaces.
using System;
// First interface defines printing behavior.
interface IPrintable
{
void Print();
}
// Second interface defines saving behavior.
interface ISavable
{
void Save();
}
// One class implements both interfaces.
class Document : IPrintable, ISavable
{
public void Print()
{
Console.WriteLine("Document printed.");
}
public void Save()
{
Console.WriteLine("Document saved.");
}
}
class Program
{
static void Main()
{
Document doc = new Document();
// Behavior required by IPrintable.
doc.Print();
// Behavior required by ISavable.
doc.Save();
}
}The Document class receives two separate contracts. It must provide the required implementation for both interfaces.
Class vs Interface
| Basis | Class | Interface |
|---|---|---|
| Main purpose | Defines an object type with state and behavior | Defines a contract or capability |
| Object creation | A non-abstract class can be instantiated | An interface itself is not directly instantiated |
| Instance fields | Can contain instance fields | Does not define ordinary instance storage fields |
| Constructors | Can have constructors | Does not use instance constructors to create interface objects |
| Inheritance | A class can directly inherit from one base class | A class can implement multiple interfaces |
| Role | Provides concrete state and implementation | Specifies capabilities that implementing types expose |
Modern C# allows interfaces to contain additional member forms, including default implementations in supported language/runtime versions. The core introductory model remains that an interface defines a contract that implementing types follow.
Let's Clarify
A university may require different people to carry different identification cards. A student card and an employee card do not make the student and employee the same kind of person, but both satisfy an identification requirement. An interface works in a similar way: unrelated classes can promise to provide the same capability without sharing the same base-class implementation.
Common Mistake
Implementing two interfaces is not the same as inheriting implementation from two classes. C# permits a class to implement multiple interface contracts, but a class still has at most one direct base class.
Summary
- An interface defines a contract that implementing types must satisfy.
- A class implements an interface using a colon after the class name.
- Interfaces allow unrelated classes to expose a common set of capabilities.
- A class may implement multiple interfaces.
- C# does not support multiple inheritance of classes, but multiple interface implementation provides multiple contracts.
- Classes contain object state and implementation, while interfaces primarily define required capabilities.
Test Yourself
- Explain how an interface creates a common contract between unrelated classes.
- Differentiate between class and interface? Why do we need serialization in file handling in C#? Write a C# program to save Student record (roll, name, address, and phone number) into file "student.txt" and then display student record. (2025)
- What are the two usages of interface? Write a program to achieve multiple inheritance in C# (2021)
Hints:
- Different classes implement the same interface members, allowing client code to use the common interface type.
- Class creates object types and can contain state; interface defines a contract. Serialization converts object state into a storable representation. A complete program appears under Serialization.
- Two uses: common contract and multiple interface implementation. Declare two interfaces and implement both in one class.
1.5 Access Modifiers
Definition
Access modifiers are C# keywords that control where a type or member can be accessed from.
Access modifiers decide who is allowed to use a class member. Some members can be available everywhere, while others may be restricted to the same class, related classes, or the same assembly.
Detailed Explanation
Access control supports encapsulation. Internal data can be hidden from unrelated code while selected operations are exposed through public members.
Common Access Modifiers
| Modifier | Accessible From | Typical Use |
|---|---|---|
| public | Any code that can access the containing type | Members intended for general use |
| private | Only inside the containing type | Internal implementation details |
| protected | Containing type and derived classes | Members intended for inheritance |
| internal | Code in the same assembly | Members or types used internally by one application/library |
| protected internal | Same assembly or derived classes in other assemblies | Combines assembly access with inheritance access |
| private protected | Containing type and derived types in the same assembly | Restricts inherited access to the same assembly |
Example
using System;
class BankAccount
{
// Can be accessed only inside BankAccount.
private double balance;
// Can be accessed from outside through an object.
public void Deposit(double amount)
{
if (amount > 0)
{
balance += amount;
}
}
// Public method provides controlled read access.
public double GetBalance()
{
return balance;
}
}
class Program
{
static void Main()
{
BankAccount account = new BankAccount();
// Allowed because Deposit is public.
account.Deposit(5000);
// Direct access to balance is not allowed because it is private.
Console.WriteLine(account.GetBalance());
}
}Default Accessibility
At namespace level, a class without an explicit access modifier is normally internal. A class member such as a field or method without an explicit access modifier is normally private.
Access Modifiers and Encapsulation
Suppose a bank account allowed any external code to assign its balance directly. A statement could accidentally set the balance to an invalid value. Making the field private forces changes to pass through controlled methods or properties.
External request → public method/property → validation → private data update
Common Mistake
protected does not mean that every class in the same program can access the member. It gives access through the containing type and derived-class relationship.
Summary
- Access modifiers control the visibility of types and members.
- public provides broad access, while private restricts access to the containing type.
- protected supports controlled access from derived classes.
- internal limits access to the same assembly.
- Access modifiers support encapsulation by hiding implementation details.
Test Yourself
- Differentiate public, private, protected, and internal access modifiers.
- How do access modifiers support encapsulation in C#?
Hints:
- Public = broad access; private = containing type only; protected = containing/derived types; internal = same assembly.
- They hide internal state and expose only selected operations, reducing uncontrolled access.
1.6 Arrays
Definition
An array is a fixed-length collection of elements of the same type stored under one variable name and accessed using indexes.
An array lets one variable hold several related values. Instead of declaring mark1, mark2, mark3, mark4, and mark5, a program can store all five values in one array and access them by position.
Detailed Explanation
Array indexes in C# begin at 0. An array containing five elements therefore uses indexes 0 through 4.
int[] marks = { 70, 80, 65, 90, 75 };
Console.WriteLine(marks[0]); // 70
Console.WriteLine(marks[4]); // 75One-Dimensional Array
A one-dimensional array stores values in a single indexed sequence.
using System;
class Program
{
static void Main()
{
int[] numbers = new int[5];
// Store values at different indexes.
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;
numbers[3] = 40;
numbers[4] = 50;
// Display every element.
for (int i = 0; i < numbers.Length; i++)
{
Console.WriteLine(numbers[i]);
}
}
}Multidimensional Array
A multidimensional array uses more than one index. A rectangular two-dimensional array is commonly used to represent rows and columns.
int[,] marks =
{
{ 70, 80, 90 },
{ 65, 75, 85 },
{ 60, 70, 80 }
};
Console.WriteLine(marks[1, 2]); // 85The declaration int[,] represents a rectangular two-dimensional array. Every row contains the same number of columns.
Jagged or Variable-Size Array
A jagged array is an array whose elements are themselves arrays. The inner arrays can have different lengths.
This structure is commonly used when different rows need different numbers of elements. In introductory questions it may be described as a variable-size array.
int[][] data = new int[3][];
data[0] = new int[] { 10, 20, 30 };
data[1] = new int[] { 40, 50 };
data[2] = new int[] { 60, 70, 80, 90 };Rectangular vs Jagged Arrays
| Basis | Rectangular Multidimensional Array | Jagged Array |
|---|---|---|
| Syntax | int[,] | int[][] |
| Row length | Same number of columns in every row | Each inner array may have a different length |
| Access | a[row, column] | a[row][column] |
| Suitable for | Tables and matrices | Rows containing different numbers of items |
Program: Marks of Three Students with Different Numbers of Subjects
The first two students have three subjects each, while the third student has only two. A rectangular two-dimensional array cannot naturally represent different row lengths, so a jagged array is the correct C# structure.
using System;
class Program
{
static void Main()
{
// Create a jagged array for three students.
// Each inner array stores the marks of one student.
int[][] marks = new int[3][];
// Student 1 has marks in three subjects.
marks[0] = new int[] { 75, 80, 85 };
// Student 2 also has marks in three subjects.
marks[1] = new int[] { 68, 72, 78 };
// Student 3 has marks in only two subjects.
marks[2] = new int[] { 88, 92 };
// Process one student at a time.
for (int i = 0; i < marks.Length; i++)
{
int total = 0;
Console.WriteLine("Student " + (i + 1));
// Display marks and calculate total.
for (int j = 0; j < marks[i].Length; j++)
{
Console.WriteLine(
"Subject " + (j + 1) + ": " + marks[i][j]
);
total += marks[i][j];
}
// Divide by the actual number of subjects
// taken by the current student.
double average =
(double)total / marks[i].Length;
Console.WriteLine("Average: " + average);
Console.WriteLine();
}
}
}How the Program Works
Step 1 → Create an outer array for three students.
Step 2 → Create an inner array of three elements for Student 1.
Step 3 → Create another three-element array for Student 2.
Step 4 → Create a two-element array for Student 3.
Step 5 → Use an outer loop to visit each student.
Step 6 → Use an inner loop to display and total that student's marks.
Step 7 → Calculate average using that particular student's subject count.
The average formula used is:
Average marks = Total marks obtained ÷ Number of subjects
Common Mistake
A rectangular array such as int[3,3] gives every student the same number of subject positions. When rows genuinely have different lengths, int[][] is the appropriate C# representation.
Summary
- An array stores multiple values of the same element type.
- Array indexing begins at zero.
- A one-dimensional array stores a sequence of elements.
- A rectangular multidimensional array uses multiple indexes and equal row dimensions.
- A jagged array contains inner arrays that may have different lengths.
- Average marks are calculated using the actual number of subjects in each student's inner array.
Test Yourself
- Differentiate between a rectangular two-dimensional array and a jagged array.
- What is variable size array? Write a C# program to create multidimensional array to store the marks of three student in different subjects. First student has marks of 3 subjects, Second student has marks of 3 subjects and Third student has marks of 2 subjects. Display the subject marks and average marks for each student. [1+4] (2026)
Hints:
int[,]has fixed rectangular dimensions;int[][]lets each inner array have a different length.- Use a jagged array with lengths 3, 3, and 2. Loop through each student's inner array, calculate total, then divide by that student's subject count.
1.7 Inheritance
Definition
Inheritance is an object-oriented mechanism in which one class acquires accessible members and behavior from another class and may add or modify its own behavior.
Inheritance allows a new class to reuse an existing class instead of rebuilding common features. A Student and Teacher may both inherit shared information from a Person class and then add their own specialized members.
Detailed Explanation
The existing class is called the base class or parent class. The class that inherits from it is called the derived class or child class.
class Person
{
public string Name;
}
class Student : Person
{
public int Roll;
}Here, Person is the base class and Student is the derived class.
Inheritance Example
using System;
class Person
{
public string Name;
public void ShowName()
{
Console.WriteLine("Name: " + Name);
}
}
class Student : Person
{
public int Roll;
public void ShowRoll()
{
Console.WriteLine("Roll: " + Roll);
}
}
class Program
{
static void Main()
{
Student s = new Student();
// Name and ShowName are inherited from Person.
s.Name = "Nisha";
s.Roll = 15;
s.ShowName();
s.ShowRoll();
}
}Types of Inheritance Relevant to C#
1. Single Inheritance
One derived class inherits directly from one base class.
Person → Student
2. Multilevel Inheritance
A class inherits from a class that already inherits from another class.
Person → Student → GraduateStudent
3. Hierarchical Inheritance
Multiple classes inherit from the same base class.
Person → Student
Person → Teacher
4. Multiple Inheritance Through Interfaces
C# does not allow a class to inherit from two base classes directly. As discussed in 1.4 Interface, one class can implement several interfaces.
interface ICamera
{
void TakePhoto();
}
interface IMusicPlayer
{
void PlayMusic();
}
class SmartDevice : ICamera, IMusicPlayer
{
public void TakePhoto()
{
Console.WriteLine("Photo captured.");
}
public void PlayMusic()
{
Console.WriteLine("Playing music.");
}
}Inheritance Forms Compared
| Type | Structure | Supported with Classes in C# |
|---|---|---|
| Single | A → B | Yes |
| Multilevel | A → B → C | Yes |
| Hierarchical | A → B and A → C | Yes |
| Multiple class inheritance | A + B → C | No |
| Multiple interface implementation | Interface A + Interface B → C | Yes |
The base Keyword
The base keyword provides access to base-class members and can be used to invoke a base-class constructor.
using System;
class Person
{
protected string name;
public Person(string n)
{
name = n;
}
}
class Student : Person
{
private int roll;
public Student(string n, int r) : base(n)
{
roll = r;
}
public void Display()
{
Console.WriteLine("Name: " + name);
Console.WriteLine("Roll: " + roll);
}
}Method Overriding
A base class can mark a method with virtual, and a derived class can provide a specialized implementation using override.
using System;
class Animal
{
public virtual void Sound()
{
Console.WriteLine("Animal sound");
}
}
class Dog : Animal
{
public override void Sound()
{
Console.WriteLine("Dog barks");
}
}Summary
- Inheritance creates a base-class and derived-class relationship.
- Derived classes reuse accessible members of the base class.
- C# supports single, multilevel, and hierarchical class inheritance.
- C# does not support inheritance from multiple base classes.
- Multiple interfaces can be implemented by one class.
- The base keyword accesses base-class members or constructors.
Test Yourself
- Explain single, multilevel, and hierarchical inheritance with suitable structures.
- Why is multiple inheritance of classes not used directly in C#, and how can multiple contracts be achieved?
- What are the two usages of interface? Write a program to achieve multiple inheritance in C# (2021)
Hints:
- Single: A→B; multilevel: A→B→C; hierarchical: one base class with several derived classes.
- A C# class has one direct base class. Multiple interface implementation lets one class satisfy several contracts.
- Use two interfaces and implement both in a single class.
1.8 Exception Handling
Definition
Exception handling is the mechanism used to detect and respond to abnormal conditions that occur while a program is running so that the program can recover, report the problem, or terminate in a controlled way.
A program may face situations it cannot handle normally, such as dividing by zero, opening a missing file, or converting invalid text into a number. C# represents many such runtime problems as exception objects. The program can catch those exceptions and decide what to do next.
Detailed Explanation
An exception is an object representing an error or abnormal runtime condition. Exception classes ultimately derive from System.Exception.
C# exception handling commonly uses:
- try
- catch
- finally
- throw
try Block
The try block contains code that may produce an exception.
try
{
int result = 10 / number;
}catch Block
A catch block handles a matching exception produced by the try block.
catch (DivideByZeroException ex)
{
Console.WriteLine(ex.Message);
}Several catch blocks can be used to handle different exception types.
finally Block
The finally block contains code intended to run when control leaves the try/catch structure, whether an exception occurred or not, subject to exceptional process-termination conditions.
It has traditionally been used for cleanup operations such as releasing resources. Modern C# commonly uses using statements or declarations for disposable resources such as streams.
Basic try-catch-finally Program
using System;
class Program
{
static void Main()
{
try
{
Console.Write("Enter first number: ");
int a = int.Parse(Console.ReadLine());
Console.Write("Enter second number: ");
int b = int.Parse(Console.ReadLine());
// This may throw DivideByZeroException.
int result = a / b;
Console.WriteLine("Result: " + result);
}
catch (FormatException)
{
// Handles invalid integer input.
Console.WriteLine("Please enter valid integers.");
}
catch (DivideByZeroException)
{
// Handles division by zero.
Console.WriteLine("Division by zero is not allowed.");
}
finally
{
// Executed after normal completion or handled exception.
Console.WriteLine("Execution of the operation is complete.");
}
}
}throw Keyword
The throw keyword explicitly raises an exception.
using System;
class Program
{
static void CheckAge(int age)
{
// Reject an invalid value by throwing an exception.
if (age < 0)
{
throw new ArgumentException("Age cannot be negative.");
}
Console.WriteLine("Age: " + age);
}
static void Main()
{
try
{
CheckAge(-5);
}
catch (ArgumentException ex)
{
Console.WriteLine("Error: " + ex.Message);
}
}
}throw; and throw ex;
Inside a catch block, C# allows an existing exception to be rethrown.
catch (Exception)
{
// Rethrow the current exception.
throw;
}throw; rethrows the current exception while preserving its original exception context more appropriately than explicitly throwing the caught variable again.
About the “throws” Keyword in the Syllabus
Standard C# has a throw keyword, but it does not have a throws keyword for declaring checked exceptions in a method signature.
The throws keyword belongs to Java syntax. For example, Java can declare that a method may throw an exception using a throws clause. C# does not require or support that method-signature mechanism.
| Point | throw in C# | throws |
|---|---|---|
| Status in C# | Valid C# keyword | Not a C# keyword |
| Purpose | Explicitly raises an exception | Used in Java to declare possible exceptions from a method |
| Example | throw new Exception("Error"); | Not valid C# method syntax |
| C# alternative | Throw exceptions where required and document expected exceptions | No equivalent mandatory checked-exception declaration |
Exception Flow
Execute try block → exception occurs → remaining try statements are skipped → runtime searches for matching catch → matching catch executes → finally executes → program continues after structure if possible
If no suitable handler is found, the exception propagates through the call stack. If it remains unhandled, the application normally terminates and reports the unhandled exception.
Let's Clarify
A payment counter may follow a normal procedure for every customer. If a card is rejected, the normal transaction path cannot continue. Instead, the failure is redirected to a special handling path. A try block represents the normal path, catch represents the response to a recognized failure, and finally represents cleanup that must still occur after the attempt.
Common Mistake
throws should not be presented as a valid C# keyword. C# uses throw to raise exceptions and does not use Java-style checked-exception declarations with throws.
Summary
- An exception represents an abnormal runtime condition.
- try contains risky operations and catch handles matching exceptions.
- finally provides a place for cleanup code after the try/catch operation.
- throw explicitly raises an exception.
throw;can rethrow the current caught exception.- Standard C# has no throws keyword equivalent to Java's method declaration clause.
Test Yourself
- Explain the execution flow of try, catch, and finally.
- Write a C# method that throws an exception when a negative value is supplied.
- Differentiate throw and throws keyword. Write a program to copy contents of one file to another using binary reader and binary writer. (2021)
Hints:
- Risky code runs in try; matching catch handles an exception; finally performs cleanup after the attempt.
- Check the argument and use
throw new ArgumentException(...)when the value is negative. - In standard C#, throw is valid but throws is not a C# keyword. The binary-copy portion uses BinaryReader and BinaryWriter, covered in 1.13.
1.9 Multithreading
Definition
A thread is an independent path of execution inside a process. Multithreading means using two or more threads so that different execution paths can make progress within the same application.
A running program can contain more than one sequence of work. One thread may handle the application's main work while another performs a separate task. The operating system and runtime schedule those threads for execution.
Detailed Explanation
A process is a running program with its own resources and address space. A process can contain one or more threads. Threads belonging to the same process share process resources, which makes communication convenient but also means shared data must be handled carefully.
Every normal C# console program begins with a main thread. Additional threads can be created when concurrent execution is required.
Creating a Thread
The System.Threading namespace contains the Thread class.
using System.Threading;
Thread t = new Thread(Worker);
t.Start();The method passed to the Thread object contains the work that the new thread executes.
Important Thread Members
| Member | Purpose |
|---|---|
| Start() | Requests execution of a newly created thread |
| Sleep() | Pauses the current thread for a specified period |
| Join() | Causes the calling thread to wait for another thread to finish |
| IsAlive | Indicates whether a thread has been started and has not yet terminated |
| Name | Allows a descriptive name to be assigned to a thread |
Thread.Sleep()
Thread.Sleep() suspends the currently executing thread for at least the requested time interval before it becomes eligible to run again.
// Pause the current thread for approximately 1000 milliseconds.
Thread.Sleep(1000);One thousand milliseconds equals one second.
Multithreaded Program with Main Thread
using System;
using System.Threading;
class Program
{
static void WorkerOne()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Worker 1: " + i);
// Pause this worker thread for 500 milliseconds.
Thread.Sleep(500);
}
}
static void WorkerTwo()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine("Worker 2: " + i);
// Pause this worker thread for 700 milliseconds.
Thread.Sleep(700);
}
}
static void Main()
{
// Main() is already executing on the main thread.
Console.WriteLine("Main thread started.");
// Create two additional threads.
Thread t1 = new Thread(WorkerOne);
Thread t2 = new Thread(WorkerTwo);
// Start both worker threads.
t1.Start();
t2.Start();
// Main thread also performs its own work.
for (int i = 1; i <= 3; i++)
{
Console.WriteLine("Main thread: " + i);
Thread.Sleep(300);
}
// Wait until both worker threads finish.
t1.Join();
t2.Join();
Console.WriteLine("All threads completed.");
}
}How the Program Runs
Step 1 → Main() begins on the main thread.
Step 2 → Two Thread objects are created.
Step 3 → t1.Start() makes WorkerOne eligible to execute.
Step 4 → t2.Start() makes WorkerTwo eligible to execute.
Step 5 → Main thread, WorkerOne, and WorkerTwo can make progress concurrently.
Step 6 → Each Sleep() temporarily pauses the thread that calls it.
Step 7 → Main calls Join() and waits until both workers complete.
The exact order of output can vary between runs because thread scheduling is controlled by the runtime and operating system.
Advantages of Multithreading
- Responsiveness: long-running work can occur without blocking another execution path.
- Concurrency: different tasks can progress during overlapping periods.
- Resource sharing: threads in the same process can access common process data.
- Parallelism: suitable work may execute on different processor cores.
Problems in Multithreading
- Race condition: result depends on the unpredictable timing of concurrent accesses to shared data.
- Synchronization complexity: shared mutable resources may need controlled access.
- Deadlock: threads can wait indefinitely for resources held by one another.
- Debugging difficulty: timing-dependent errors may not happen consistently.
Let's Clarify
A restaurant kitchen can prepare rice, vegetables, and drinks through different workers instead of requiring one person to finish every item sequentially. The workers share the same kitchen, so coordination is still necessary when they need the same equipment. Threads similarly share process resources while following separate execution paths.
Common Mistake
Thread.Sleep() pauses only the thread that calls it. Calling Sleep() inside WorkerOne does not directly pause WorkerTwo or the main thread.
Summary
- A thread is a path of execution within a process.
- Multithreading allows multiple execution paths in one application.
- A C# program begins with a main thread and may create additional Thread objects.
- Start() begins a thread, Sleep() pauses the current thread, and Join() waits for another thread.
- The scheduling order of concurrent threads is not guaranteed.
- Shared data can cause race conditions and therefore may require synchronization.
Test Yourself
- What is the purpose of Start(), Sleep(), and Join() in thread programming?
- What is thread? Write a C# program to create multiple thread along with main Thread. (2025)
- Explain thread lifecycle. Write a multithreaded program using C#. (2023)
- a) Write a C# program to demonstrate multithreading. You must use sleep() method in your program. (2021)
Hints:
- Start = begin thread execution; Sleep = pause current thread temporarily; Join = wait for another thread to terminate.
- Define thread as an execution path and create two Thread objects while Main continues on the main thread.
- Explain Unstarted → Running → Wait/Sleep/Join when blocked → Running → Stopped, then provide a Thread-based program.
- Create at least two threads and call
Thread.Sleep(milliseconds)inside their work methods.
1.10 Thread Lifecycle
Definition
The thread lifecycle describes the states and transitions a thread passes through from creation until its execution ends.
A thread does not execute continuously from the moment its object is created. It begins unstarted, becomes runnable after Start(), may temporarily wait or sleep, can resume, and eventually terminates when its method finishes.
Detailed Explanation
The lifecycle can be represented conceptually as:
Created/Unstarted → Started/Running → Waiting, Sleeping, or Joining when necessary → Running again → Stopped
1. Unstarted State
A newly created Thread object is in an unstarted condition. The thread has been created but its target method has not begun execution.
Thread t = new Thread(Worker);At this point, t.Start() has not yet been called.
2. Running or Runnable Execution
After Start() is called, the thread is made eligible for execution. The operating system scheduler determines when it receives processor time.
t.Start();The word “running” is often used in introductory lifecycle diagrams for this active stage, although actual CPU scheduling continuously moves execution among runnable threads.
3. Wait, Sleep, or Join
A thread can temporarily stop making progress because it is waiting.
Common reasons include:
- Sleep: the thread calls Thread.Sleep().
- Join: the thread waits for another thread to complete.
- Synchronization wait: the thread waits for a synchronization condition or resource.
The ThreadState enumeration historically exposes a WaitSleepJoin state flag representing several of these waiting conditions.
4. Return to Execution
After the waiting condition ends, the thread becomes eligible to run again.
Sleep interval ends → scheduler makes thread runnable → thread receives CPU time → execution continues after Sleep()
5. Stopped State
When the thread's target method returns normally, the thread has completed its execution and reaches the stopped/terminated stage.
A Thread object that has finished cannot be restarted by calling Start() again.
Thread Lifecycle Example
using System;
using System.Threading;
class Program
{
static void Worker()
{
Console.WriteLine("Worker is running.");
// Current worker enters a sleeping/waiting period.
Thread.Sleep(1000);
Console.WriteLine("Worker resumed.");
// Returning from this method ends the worker thread.
}
static void Main()
{
// Thread object exists, but has not been started.
Thread t = new Thread(Worker);
Console.WriteLine("Thread created.");
// Thread becomes eligible to execute.
t.Start();
// Main thread waits for worker to finish.
t.Join();
Console.WriteLine("Worker thread finished.");
}
}Lifecycle Table
| Stage | Meaning | Typical Cause |
|---|---|---|
| Unstarted | Thread object exists but execution has not begun | new Thread(...) |
| Running/Runnable | Thread is eligible for or receiving execution time | Start() |
| Wait/Sleep/Join | Thread temporarily waits | Sleep(), Join(), synchronization |
| Running again | Thread becomes eligible after waiting | Wait condition finishes |
| Stopped | Thread execution has completed | Target method returns |
Common Mistake
Calling Start() does not guarantee that the new thread immediately runs before the next statement on the calling thread. Start() makes it eligible for scheduling; the actual execution order is determined by the scheduler.
Summary
- A thread begins in an unstarted state after its Thread object is created.
- Start() makes the thread eligible for execution.
- Sleep(), Join(), and synchronization can temporarily place a thread in a waiting condition.
- A waiting thread may resume when its waiting condition finishes.
- The thread terminates when its target method completes.
- A completed Thread object cannot be started a second time.
Test Yourself
- Describe the lifecycle of a thread using an ordered sequence.
- What happens to a thread when Thread.Sleep() is executed?
- Explain thread lifecycle. Write a multithreaded program using C#. (2023)
Hints:
- Unstarted → Running/Runnable → Wait/Sleep/Join if needed → Running again → Stopped.
- The calling thread waits for the specified interval, then becomes eligible to be scheduled again.
- Explain lifecycle states and demonstrate at least two concurrently executing threads with the Thread class.
1.11 File Input/Output and FileStream
Definition
File input/output, usually written as File I/O, is the process of reading data from files and writing data to files so information can persist outside program memory. FileStream is a C# stream class used to read or write raw bytes in a file.
Variables disappear when a program finishes, but information stored in a file remains available for later use. C# provides stream classes for moving data between a program and files stored on a device.
Detailed Explanation
The System.IO namespace provides the principal file and stream classes used in basic C# file handling.
A stream is a sequence of data flowing between a program and some source or destination.
For file reading:
File on storage → input stream → C# program
For file writing:
C# program → output stream → file on storage
Common File Handling Classes
| Class | Purpose |
|---|---|
| File | Provides static methods for common file operations such as reading, writing, copying, deleting, and checking existence |
| FileInfo | Represents a file and provides instance-based file information and operations |
| Directory | Provides static operations for directories |
| DirectoryInfo | Represents a directory and exposes instance-based operations |
| Path | Provides methods for manipulating file and directory path strings |
| FileStream | Reads and writes bytes in files |
| StreamReader | Reads characters and text from a stream |
| StreamWriter | Writes characters and text to a stream |
| BinaryReader | Reads primitive values in binary form from a stream |
| BinaryWriter | Writes primitive values in binary form to a stream |
Text Files and Binary Files
A text file represents information as encoded characters. A binary file stores data as bytes according to a binary format rather than representing every value as human-readable text.
| Basis | Text File | Binary File |
|---|---|---|
| Representation | Characters encoded using a text encoding | Raw bytes structured according to a binary format |
| Human readability | Usually readable in a text editor | Usually not meaningful when opened as plain text |
| Common classes | StreamReader, StreamWriter | BinaryReader, BinaryWriter, FileStream |
| Typical data | Logs, configuration text, comma-separated values, plain records | Images, audio, compiled data, encoded primitive values |
| Conversion | Numbers are often converted to and from character strings | Primitive values can be stored directly according to a binary representation |
FileStream
FileStream works with bytes and provides low-level access to a file.
A common constructor specifies:
- File path
- FileMode
- FileAccess
Common FileMode Values
| FileMode | Meaning |
|---|---|
| Create | Creates a new file or overwrites an existing one |
| CreateNew | Creates a new file and fails if the file already exists |
| Open | Opens an existing file |
| OpenOrCreate | Opens the file if present or creates it if absent |
| Append | Opens or creates a file for adding data at the end |
| Truncate | Opens an existing file and reduces its length to zero |
Common FileAccess Values
- Read: file is opened for reading.
- Write: file is opened for writing.
- ReadWrite: both reading and writing are allowed.
FileStream Program
using System;
using System.IO;
using System.Text;
class Program
{
static void Main()
{
string message = "Hello from C# FileStream.";
// Convert text into bytes before writing with FileStream.
byte[] data = Encoding.UTF8.GetBytes(message);
// Create the file and open it for writing.
using (FileStream stream =
new FileStream("message.txt",
FileMode.Create,
FileAccess.Write))
{
// Write all bytes into the file.
stream.Write(data, 0, data.Length);
}
Console.WriteLine("Data written successfully.");
}
}Why using Is Used with Streams
File streams use operating-system resources. They should be disposed when work is complete. A using statement guarantees disposal when execution leaves the block through normal control flow or an exception.
Open stream → perform I/O → leave using block → Dispose() called → file handle released
File Class Convenience Methods
For short operations, the File class provides convenient methods.
using System.IO;
// Write complete text to a file.
File.WriteAllText("note.txt", "C# File I/O");
// Read complete text from a file.
string content = File.ReadAllText("note.txt");Let's Clarify
A stream acts like a pipeline carrying data. FileStream carries bytes through that pipeline. StreamReader and StreamWriter add text interpretation on top of a stream, while BinaryReader and BinaryWriter add methods for reading and writing binary primitive values.
Common Mistake
A file with a .txt extension is not automatically guaranteed to contain text. The actual bytes and format determine whether the content is textual or binary. File extensions are conventions used to describe expected content.
Summary
- File I/O allows programs to store and retrieve persistent data.
- System.IO contains the main C# file and stream classes.
- FileStream provides byte-oriented file access.
- Text files represent character data, while binary files store data according to binary formats.
- FileMode controls how a file is opened or created, and FileAccess controls permitted operations.
- Streams should be disposed after use, commonly through a using statement.
Test Yourself
- Explain the role of FileStream, FileMode, and FileAccess.
- What are difference between text files and binary files? List commonly used classes in C# for file handling. Write a C# console application to save Student record (roll, name, marks of 5 subjects) into file “student.txt” and then display student record with average marks. [2+1+7] (2026)
Hints:
- FileStream transfers bytes; FileMode determines create/open behavior; FileAccess specifies read/write permission.
- Text = character representation; binary = byte-oriented format. Common classes include File, FileInfo, FileStream, StreamReader, StreamWriter, BinaryReader, and BinaryWriter. The complete Student text-file program appears in 1.12.
1.12 StreamReader and StreamWriter
Definition
StreamReader is a class used to read character data from a text stream, while StreamWriter is a class used to write character data to a text stream.
When a program works with ordinary readable text, StreamWriter can place lines of text into a file and StreamReader can retrieve those lines later.
Detailed Explanation
Both classes are available in the System.IO namespace.
StreamWriter
Common StreamWriter methods include:
- Write(): writes text without automatically adding a line break.
- WriteLine(): writes text followed by a line terminator.
- Flush(): pushes buffered data toward the underlying stream.
- Close(): closes the writer, although a using statement is generally preferred for automatic disposal.
StreamReader
Common StreamReader members include:
- Read(): reads the next character.
- ReadLine(): reads one line of text.
- ReadToEnd(): reads all remaining text.
- EndOfStream: indicates whether the end of the stream has been reached.
Basic Writing and Reading Program
using System;
using System.IO;
class Program
{
static void Main()
{
// Write text to the file.
using (StreamWriter writer =
new StreamWriter("message.txt"))
{
writer.WriteLine("C# File Handling");
writer.WriteLine("StreamWriter writes text.");
}
// Read the text back from the file.
using (StreamReader reader =
new StreamReader("message.txt"))
{
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
}
}Program: Store Student Record and Display Average Marks
This program stores the student's roll number, name, and marks of five subjects in student.txt. It then reopens the file, reads the saved data, calculates the total and average, and displays the complete record.
using System;
using System.IO;
class Program
{
static void Main()
{
Console.Write("Enter roll number: ");
int roll = int.Parse(Console.ReadLine());
Console.Write("Enter student name: ");
string name = Console.ReadLine();
// Array stores marks of five subjects.
int[] marks = new int[5];
for (int i = 0; i < marks.Length; i++)
{
Console.Write(
"Enter marks of subject " + (i + 1) + ": "
);
marks[i] = int.Parse(Console.ReadLine());
}
// Save student information in a text file.
using (StreamWriter writer =
new StreamWriter("student.txt"))
{
writer.WriteLine(roll);
writer.WriteLine(name);
// Save one mark on each following line.
for (int i = 0; i < marks.Length; i++)
{
writer.WriteLine(marks[i]);
}
}
Console.WriteLine();
Console.WriteLine("Record saved successfully.");
Console.WriteLine();
// Variables that will receive data read from the file.
int savedRoll;
string savedName;
int[] savedMarks = new int[5];
// Open the text file and read the record.
using (StreamReader reader =
new StreamReader("student.txt"))
{
savedRoll = int.Parse(reader.ReadLine());
savedName = reader.ReadLine();
for (int i = 0; i < savedMarks.Length; i++)
{
savedMarks[i] =
int.Parse(reader.ReadLine());
}
}
// Calculate total marks.
int total = 0;
for (int i = 0; i < savedMarks.Length; i++)
{
total += savedMarks[i];
}
// Average = total marks / number of subjects.
double average =
(double)total / savedMarks.Length;
// Display the saved record.
Console.WriteLine("Student Record");
Console.WriteLine("Roll: " + savedRoll);
Console.WriteLine("Name: " + savedName);
for (int i = 0; i < savedMarks.Length; i++)
{
Console.WriteLine(
"Subject " + (i + 1) +
" Marks: " + savedMarks[i]
);
}
Console.WriteLine("Total: " + total);
Console.WriteLine("Average: " + average);
}
}Program Flow
Input roll, name, and five marks → create StreamWriter → save values → close writer → create StreamReader → read values → calculate total → calculate average → display record
The calculation is:
Total = Mark1 + Mark2 + Mark3 + Mark4 + Mark5
Average = Total ÷ 5
Appending Text
StreamWriter can append rather than overwrite by using an appropriate constructor option.
using (StreamWriter writer =
new StreamWriter("log.txt", true))
{
// true means append to the existing file.
writer.WriteLine("New log entry");
}Common Mistake
Opening new StreamWriter("student.txt") without append mode overwrites the existing file content. Use append mode only when previous records are meant to remain in the file.
Summary
- StreamWriter writes character data to text streams.
- StreamReader reads character data from text streams.
- WriteLine() stores a line, while ReadLine() retrieves one line.
- using blocks ensure readers and writers are disposed when work is complete.
- Student records can be stored line by line and reconstructed in the same order.
- Average marks equal total marks divided by the number of subjects.
Test Yourself
- Write a C# program that writes three lines to a text file and then displays them using StreamReader.
- What are difference between text files and binary files? List commonly used classes in C# for file handling. Write a C# console application to save Student record (roll, name, marks of 5 subjects) into file “student.txt” and then display student record with average marks. [2+1+7] (2026)
- Differentiate between class and interface? Why do we need serialization in file handling in C#? Write a C# program to save Student record (roll, name, address, and phone number) into file "student.txt" and then display student record. (2025)
Hints:
- Use StreamWriter with WriteLine(), dispose it, then StreamReader with ReadLine() or ReadToEnd().
- Store roll, name, and five marks with StreamWriter. Read them back, total the marks, and divide by five.
- A plain-text version can save each field with StreamWriter. The serialization-based version is developed in 1.14.
1.13 BinaryReader and BinaryWriter
Definition
BinaryReader reads primitive data types from a binary stream, while BinaryWriter writes primitive values and byte sequences to a binary stream.
Text readers work with characters. BinaryReader and BinaryWriter work with data represented in binary form and provide methods for values such as integers, floating-point numbers, Boolean values, strings in the writer's format, and raw byte arrays.
Detailed Explanation
BinaryReader and BinaryWriter usually operate on an underlying stream such as FileStream.
For writing:
Program values → BinaryWriter → FileStream → file
For reading:
File → FileStream → BinaryReader → program values
Writing Binary Data
using System;
using System.IO;
class Program
{
static void Main()
{
using (FileStream stream =
new FileStream("data.bin",
FileMode.Create,
FileAccess.Write))
using (BinaryWriter writer =
new BinaryWriter(stream))
{
// Write primitive values in binary form.
writer.Write(101);
writer.Write("Anita");
writer.Write(78.5);
}
Console.WriteLine("Binary data written.");
}
}Reading Binary Data
The values must normally be read in the same logical sequence and compatible types used when they were written.
using System;
using System.IO;
class Program
{
static void Main()
{
using (FileStream stream =
new FileStream("data.bin",
FileMode.Open,
FileAccess.Read))
using (BinaryReader reader =
new BinaryReader(stream))
{
// Read values in the same order used while writing.
int roll = reader.ReadInt32();
string name = reader.ReadString();
double marks = reader.ReadDouble();
Console.WriteLine("Roll: " + roll);
Console.WriteLine("Name: " + name);
Console.WriteLine("Marks: " + marks);
}
}
}Binary File Copy Using BinaryReader and BinaryWriter
To copy an arbitrary file, the program should transfer raw bytes rather than attempting to interpret the file as integers, strings, or other application-specific values.
using System;
using System.IO;
class Program
{
static void Main()
{
string sourceFile = "source.dat";
string destinationFile = "copy.dat";
// Open source file for binary reading.
using (FileStream sourceStream =
new FileStream(sourceFile,
FileMode.Open,
FileAccess.Read))
using (BinaryReader reader =
new BinaryReader(sourceStream))
// Create destination file for binary writing.
using (FileStream destinationStream =
new FileStream(destinationFile,
FileMode.Create,
FileAccess.Write))
using (BinaryWriter writer =
new BinaryWriter(destinationStream))
{
// Copy the file in blocks rather than one byte at a time.
const int bufferSize = 4096;
while (reader.BaseStream.Position
< reader.BaseStream.Length)
{
long bytesLeft =
reader.BaseStream.Length -
reader.BaseStream.Position;
int bytesToRead =
(int)Math.Min(bufferSize, bytesLeft);
// Read raw bytes from the source file.
byte[] buffer =
reader.ReadBytes(bytesToRead);
// Write the same bytes into the destination file.
writer.Write(buffer);
}
}
Console.WriteLine("File copied successfully.");
}
}How Binary Copy Works
Step 1 → Open source file through FileStream.
Step 2 → Attach BinaryReader to the source stream.
Step 3 → Create destination FileStream.
Step 4 → Attach BinaryWriter to the destination stream.
Step 5 → Read a block of bytes from the source.
Step 6 → Write the same block to the destination.
Step 7 → Repeat until the source position reaches the source length.
Step 8 → Dispose readers, writers, and streams.
Text Reader/Writer vs Binary Reader/Writer
| Basis | StreamReader/StreamWriter | BinaryReader/BinaryWriter |
|---|---|---|
| Main data form | Characters and text | Binary primitive values and byte sequences |
| Human readability | Designed for textual data | Stored format is usually not directly human-readable |
| Typical methods | ReadLine(), WriteLine() | ReadInt32(), ReadDouble(), ReadBytes(), Write() |
| Suitable for | Plain-text records and documents | Structured binary data and byte-level operations |
Common Mistake
BinaryReader does not automatically know the structure of arbitrary data. If a program writes an integer, string, and double in that order, the reader must use a compatible reading sequence. Reading with the wrong method or order produces incorrect results or errors.
Summary
- BinaryReader reads typed values and bytes from a binary stream.
- BinaryWriter writes primitive values and byte arrays to a binary stream.
- They commonly operate on FileStream objects.
- Structured binary values should be read in a compatible order with the order in which they were written.
- Arbitrary file copying should transfer raw byte blocks.
- Buffered block copying is more practical than processing one byte at a time.
Test Yourself
- Why should values written with BinaryWriter usually be read in the same logical order?
- b) Write a C# program to copy contents of one file to another using binary reader and writer. (2023)
- Differentiate throw and throws keyword. Write a program to copy contents of one file to another using binary reader and binary writer. (2021)
Hints:
- The binary stream contains values in the sequence and format written. The reader must interpret those bytes with matching methods.
- Open source with BinaryReader and destination with BinaryWriter, repeatedly ReadBytes() and Write() until the source ends.
- For the file portion, copy raw byte blocks through BinaryReader and BinaryWriter. In C#, throw is valid; throws is not a C# keyword.
1.14 Serialization
Definition
Serialization is the process of converting an object's data into a representation that can be stored or transmitted. Deserialization reconstructs usable object data from that stored representation.
A program may create a Student object containing roll number, name, address, and phone number. Serialization converts those property values into a format that can be written to a file. Later, deserialization reads that stored representation and rebuilds the Student data.
Detailed Explanation
Objects exist in program memory while an application is running. If the application needs to preserve an object's state after termination, that state must be written to persistent storage in some defined format.
The serialization process is:
Object in memory → serializer examines data → serialized representation → file/network/storage
Deserialization reverses the process:
Stored representation → deserializer → reconstructed object data in memory
Why Serialization Is Needed
- Persistence: object data can survive after the program terminates.
- Data transfer: object information can be sent between programs or systems.
- Structured storage: multiple object properties can be preserved in a defined format.
- Reconstruction: saved data can be converted back into an object representation.
- Interchange: formats such as JavaScript Object Notation (JSON) can be exchanged between applications written in different languages.
Common Serialization Formats
| Format | Characteristics | Typical Use |
|---|---|---|
| JSON | Text-based, compact, widely supported | Web applications, application settings, data exchange |
| Extensible Markup Language (XML) | Text-based and strongly structured with markup | Configuration, interoperability, structured documents |
| Custom binary format | Application-defined binary representation | Compact or specialized binary storage |
JSON Serialization in C#
Modern .NET provides System.Text.Json for JSON serialization.
Program: Save and Display Student Record Using Serialization
using System;
using System.IO;
using System.Text.Json;
class Student
{
// Public properties will be serialized.
public int Roll { get; set; }
public string Name { get; set; }
public string Address { get; set; }
public string PhoneNumber { get; set; }
}
class Program
{
static void Main()
{
// Create a Student object.
Student student = new Student();
Console.Write("Enter roll number: ");
student.Roll = int.Parse(Console.ReadLine());
Console.Write("Enter name: ");
student.Name = Console.ReadLine();
Console.Write("Enter address: ");
student.Address = Console.ReadLine();
Console.Write("Enter phone number: ");
student.PhoneNumber = Console.ReadLine();
// Convert the Student object into JSON text.
string json =
JsonSerializer.Serialize(student);
// Store the serialized representation in student.txt.
File.WriteAllText("student.txt", json);
Console.WriteLine();
Console.WriteLine("Student record saved.");
// Read serialized data from the file.
string savedJson =
File.ReadAllText("student.txt");
// Convert JSON back into a Student object.
Student savedStudent =
JsonSerializer.Deserialize<Student>(savedJson);
Console.WriteLine();
Console.WriteLine("Student Record");
Console.WriteLine(
"Roll: " + savedStudent.Roll
);
Console.WriteLine(
"Name: " + savedStudent.Name
);
Console.WriteLine(
"Address: " + savedStudent.Address
);
Console.WriteLine(
"Phone: " + savedStudent.PhoneNumber
);
}
}Program Explanation
Step 1 → Define the Student class and its properties.
Step 2 → Create a Student object.
Step 3 → Assign roll, name, address, and phone number.
Step 4 → JsonSerializer.Serialize() converts the object data into JSON text.
Step 5 → File.WriteAllText() stores the JSON representation in student.txt.
Step 6 → File.ReadAllText() loads the stored JSON.
Step 7 → JsonSerializer.Deserialize() reconstructs the Student data.
Step 8 → Display the restored properties.
Example Serialized Data
{"Roll":15,"Name":"Anita","Address":"Kathmandu","PhoneNumber":"9800000000"}The serialized representation contains property names and their values. It can be stored as text even though it represents the state of an object.
Serialization vs Ordinary File Writing
| Basis | Ordinary Manual File Writing | Serialization |
|---|---|---|
| Programmer responsibility | Programmer manually decides how each field is written | Serializer converts supported object data into a defined format |
| Structure | Can be application-specific and informal | Uses a serialization format such as JSON or XML |
| Reconstruction | Programmer manually reads and parses fields | Deserializer reconstructs data according to the target type |
| Best suited for | Small custom text or binary formats | Structured object persistence and data exchange |
Legacy Binary Serialization Note
Older C# and .NET Framework teaching materials often demonstrate object serialization using BinaryFormatter and the SerializableAttribute. BinaryFormatter is obsolete in modern .NET because unsafe deserialization can create serious security vulnerabilities. New applications should use safer serializers such as System.Text.Json, XML serialization, or an explicitly designed binary format.
Serialization Safety
Serialized input should not automatically be trusted merely because it has the expected file extension. Deserialization converts external data into program objects, so the application should validate input and use serializers appropriate for untrusted data.
Let's Clarify
Suppose a Student object contains four values in memory. Manually saving the object means writing each value according to your own file layout and later remembering that exact layout. A serializer provides a standard conversion step that represents the object's data in a structured format and later performs the reverse conversion.
Common Mistake
Serialization does not save the live execution of an object, its running methods, or the entire program state. It saves supported data representing object state according to the chosen serialization format.
Summary
- Serialization converts object data into a storable or transferable representation.
- Deserialization converts stored data back into object data.
- Serialization is used for persistence, transfer, structured storage, and reconstruction.
- JSON and XML are common text-based serialization formats.
- System.Text.Json provides modern JSON serialization in .NET.
- Legacy BinaryFormatter-based serialization is obsolete and should not be used in new applications.
Test Yourself
- Differentiate between serialization and deserialization.
- Explain why object serialization is useful in file handling.
- Differentiate between class and interface? Why do we need serialization in file handling in C#? Write a C# program to save Student record (roll, name, address, and phone number) into file "student.txt" and then display student record. (2025)
Hints:
- Serialization: object data → stored/transmitted representation. Deserialization: representation → reconstructed object data.
- It provides structured persistence of object state and allows that state to be reconstructed later.
- Class defines objects and state; interface defines a contract. Serialize a Student object, write it to student.txt, deserialize it, and display its four properties.
Exam Questions
- What is variable size array? Write a C# program to create multidimensional array to store the marks of three student in different subjects. First student has marks of 3 subjects, Second student has marks of 3 subjects and Third student has marks of 2 subjects. Display the subject marks and average marks for each student. [1+4] (2026)
- What are difference between text files and binary files? List commonly used classes in C# for file handling. Write a C# console application to save Student record (roll, name, marks of 5 subjects) into file “student.txt” and then display student record with average marks. [2+1+7] (2026)
- What is thread? Write a C# program to create multiple thread along with main Thread. (2025)
- Differentiate between class and interface? Why do we need serialization in file handling in C#? Write a C# program to save Student record (roll, name, address, and phone number) into file "student.txt" and then display student record. (2025)
- Explain thread lifecycle. Write a multithreaded program using C#. (2023)
- b) Write a C# program to copy contents of one file to another using binary reader and writer. (2023)
- Explain Net architecture in detail. (2021)
- What are the two usages of interface? Write a program to achieve multiple inheritance in C# (2021)
- Differentiate throw and throws keyword. Write a program to copy contents of one file to another using binary reader and binary writer. (2021)
- a) Write a C# program to demonstrate multithreading. You must use sleep() method in your program. (2021)
Whole Chapter Summary
1.1 .NET Architecture
.NET executes C# programs through a managed runtime. C# source is compiled into Common Intermediate Language and metadata, loaded by the Common Language Runtime, and translated into native instructions by the Just-In-Time compiler. The architecture also includes the Common Type System, Common Language Specification, libraries, assemblies, and garbage collection.
1.2 Class and Object
A class defines the data and behavior of a type, while an object is an actual instance of that class. Objects created from the same class can contain different instance values while sharing the behavior defined by the class.
1.3 Creating Classes and Objects
Classes are declared with the class keyword. Objects are commonly created with new, which allocates the object and invokes its constructor. Constructors establish the initial state of newly created objects.
1.4 Interface
An interface defines a contract that implementing classes or structures agree to provide. One class can implement multiple interfaces, allowing several capabilities to be combined without multiple inheritance of classes.
1.5 Access Modifiers
Access modifiers determine where types and members can be accessed. Public, private, protected, internal, protected internal, and private protected provide different visibility boundaries and support encapsulation.
1.6 Arrays
Arrays store multiple values of the same element type. Rectangular multidimensional arrays use fixed dimensions, while jagged arrays contain inner arrays of different lengths and are suitable for students taking different numbers of subjects.
1.7 Inheritance
Inheritance allows a derived class to reuse accessible members of a base class. C# supports single, multilevel, and hierarchical class inheritance but not multiple class inheritance; multiple contracts are achieved through interfaces.
1.8 Exception Handling
Exception handling uses try, catch, finally, and throw to respond to abnormal runtime conditions. C# provides throw for raising exceptions but does not contain Java's throws declaration keyword.
1.9 Multithreading
A thread is a path of execution inside a process. C# can create additional Thread objects beside the main thread, and Start(), Sleep(), and Join() control common aspects of their execution and coordination.
1.10 Thread Lifecycle
A thread begins unstarted, becomes runnable after Start(), may enter waiting conditions through Sleep(), Join(), or synchronization, can resume, and eventually terminates when its target method completes.
1.11 File Input/Output and FileStream
File I/O stores and retrieves persistent information. FileStream provides byte-level file access, while FileMode controls opening behavior and FileAccess specifies whether reading, writing, or both are permitted.
1.12 StreamReader and StreamWriter
StreamReader reads text and StreamWriter writes text. Student records can be stored line by line, read in the same logical order, and processed to calculate values such as total and average marks.
1.13 BinaryReader and BinaryWriter
BinaryReader and BinaryWriter operate on binary streams and support primitive values and raw byte arrays. A binary file can be copied by repeatedly reading blocks of bytes from the source and writing them unchanged to the destination.
1.14 Serialization
Serialization converts object data into a representation suitable for storage or transfer, while deserialization reconstructs that data. Modern .NET commonly uses formats such as JSON or XML rather than obsolete BinaryFormatter-based serialization.
Key Formulas and Points Sheet
| Formula, Rule, or Sequence | Meaning |
|---|---|
| Average = Total marks ÷ Number of subjects | Used to calculate the mean score for one student |
| C# source → CIL → CLR/JIT → Native code | Core managed execution sequence in .NET |
| Class → new → constructor → object | Basic object creation flow |
| DerivedClass : BaseClass | C# class inheritance syntax |
| Class : IFirst, ISecond | One class implementing multiple interfaces |
| try → exception → catch → finally | General handled exception flow |
| throw new ExceptionType(...) | Explicitly raises an exception |
| throws | Not a standard C# keyword; Java uses it in method declarations |
| new Thread(method) → Start() | Create a thread object and request execution |
| Thread.Sleep(milliseconds) | Temporarily pauses the calling thread |
| thread.Join() | Calling thread waits until the specified thread finishes |
| Unstarted → Running/Runnable → Wait/Sleep/Join → Running → Stopped | Conceptual thread lifecycle |
| Program → StreamWriter → Text file | Text-writing direction |
| Text file → StreamReader → Program | Text-reading direction |
| Program → BinaryWriter → FileStream → File | Binary-writing flow |
| File → FileStream → BinaryReader → Program | Binary-reading flow |
| Object → Serialize → Stored representation | Serialization process |
| Stored representation → Deserialize → Object data | Deserialization process |
Last-Minute Revision Sheet
| Concept | Trigger Phrase | Recall Cue |
|---|---|---|
| .NET | Managed platform | Runtime + libraries + tools |
| CLR | Execution engine | Runs managed code, memory, exceptions, threads |
| CIL | Intermediate code | C# compiler output before native execution |
| JIT | Runtime compiler | CIL → native instructions |
| CTS | Common type rules | Shared .NET type system |
| CLS | Language interoperability | Common subset of rules |
| Class | Type definition | Data + behavior |
| Object | Class instance | Actual runtime entity |
| Constructor | Object initialization | Same class name, no return type |
| Interface | Contract | Common capability, multiple implementation |
| public | Broad visibility | Accessible wherever containing type is accessible |
| private | Maximum member hiding | Containing type only |
| protected | Inheritance access | Containing + derived types |
| internal | Assembly access | Same assembly |
| Array | Same-type collection | Zero-based indexes |
int[,] | Rectangular array | Equal dimensions |
int[][] | Jagged array | Different row lengths |
| Inheritance | Reuse from base class | Base → derived |
| Multiple interfaces | Several contracts | class C : IA, IB |
| try | Risky code | Code that may throw |
| catch | Exception handler | Handles matching exception |
| finally | Cleanup path | Runs after try/catch attempt |
| throw | Raise exception | Valid C# keyword |
| throws | Java declaration | Not a standard C# keyword |
| Thread | Execution path | One process can contain many |
| Start() | Begin thread | Makes thread eligible to execute |
| Sleep() | Temporary pause | Pauses calling thread |
| Join() | Wait for thread | Caller blocks until target finishes |
| File I/O | Persistent storage | Read and write files |
| FileStream | Byte stream | Low-level file bytes |
| StreamReader | Read text | ReadLine(), ReadToEnd() |
| StreamWriter | Write text | Write(), WriteLine() |
| BinaryReader | Read binary values | ReadInt32(), ReadDouble(), ReadBytes() |
| BinaryWriter | Write binary values | Write primitive values/bytes |
| Serialization | Object → storage format | Persist or transfer object data |
| Deserialization | Storage format → object | Reconstruct stored data |


