C# Tutorial
C# Features
C# (pronounced "C-Sharp") is a modern, object-oriented, and type-safe programming language developed by Microsoft. It is part of the .NET ecosystem and is used for a wide variety of applications, including web development, mobile apps, and game development.
Here are some of the key features of C#:
- Object-Oriented Programming (OOP): C# is fully object-oriented, supporting features like inheritance, polymorphism, encapsulation, and abstraction.
- Strongly Typed Language: C# enforces type checking at compile-time, helping to catch errors early and improve code reliability.
- Cross-Platform Support: With .NET Core, C# applications can run on multiple platforms, including Windows, macOS, and Linux.
- Automatic Memory Management: C# uses garbage collection to automatically manage memory, reducing the risk of memory leaks.
- Modern Language Features: C# has modern language features like LINQ (Language Integrated Query), async/await for asynchronous programming, and pattern matching.
- Rich Library Support: C# is backed by the .NET framework, providing a rich set of libraries for tasks such as data access, UI creation, and more.
Example of C# Code
using System;
class Program
{
static void Main()
{
Console.WriteLine("Hello, C#!");
}
}
In this simple example, the Console.WriteLine method is used to print "Hello, C#!" to the console. This is just a basic example of how easy it is to get started with C#.
C# Variables
In C#, variables are used to store data, which can then be used and manipulated during program execution. C# is a strongly typed language, meaning you must declare the type of data a variable will store when defining it.
Declaring Variables
To declare a variable, you specify its type followed by the variable name. Here's an example:
int age = 25; // Declaring an integer variable
string name = "John"; // Declaring a string variable
double height = 5.9; // Declaring a double variable
In this example, we declare three variables: age, name, and height. Each variable is associated with a specific data type:
int: Represents integer values.string: Represents text or string data.double: Represents floating-point numbers with decimals.
Variable Types in C#
C# supports various data types, including:
int: For whole numbers, e.g., 1, -5, 42.double: For decimal numbers, e.g., 3.14, -2.71.string: For sequences of characters, e.g., "Hello World".bool: For boolean values, e.g., true or false.char: For single characters, e.g., 'A'.
Variable Scope
The scope of a variable in C# refers to where it is accessible within the code. Variables can have different scopes based on where they are declared:
- Local Variables: Declared inside a method, accessible only within that method.
- Instance Variables: Declared inside a class but outside any methods, accessible by any methods in the class.
- Static Variables: Declared with the
statickeyword, they belong to the class and are shared by all instances of the class.
Constants and Readonly Variables
C# also supports constants and readonly variables. Constants are immutable values, while readonly variables can only be assigned a value during their declaration or in a constructor.
const double Pi = 3.14159; // A constant value
readonly int maxAttempts = 5; // A readonly variable
The keyword const is used to define constant variables, while readonly is used to create variables that can only be assigned a value once.
Example: Using Variables in C#
using System;
class Program
{
static void Main()
{
int age = 30;
string name = "Alice";
double height = 5.7;
Console.WriteLine("Name: " + name);
Console.WriteLine("Age: " + age);
Console.WriteLine("Height: " + height);
}
}
In this example, we define a name, age, and height variable and print their values using the Console.WriteLine() method.
C# Data Types
C# provides a variety of data types to cater to different kinds of data storage and manipulation. C# has two categories of data types: Value Types and Reference Types.
Value Types
Value types directly store data. They are typically stored on the stack and hold the actual data. The main value types include:
- int: Represents a 32-bit signed integer, e.g., 42, -100.
- float: Represents a single-precision floating point number, e.g., 3.14f, -0.5f.
- double: Represents a double-precision floating point number, e.g., 3.14159, -1.234567.
- char: Represents a single Unicode character, e.g., 'A', '1'.
- bool: Represents a boolean value, either
trueorfalse. - decimal: Represents a 128-bit precise decimal value, e.g., 123.45m, used for financial calculations.
Reference Types
Reference types store references to their data, not the data itself. These types are usually stored on the heap, and the memory is managed by the garbage collector. The primary reference types are:
- string: Represents a sequence of characters, e.g., "Hello, World!".
- object: The base type for all types in C#, allowing you to store any type of data.
- array: A collection of elements of the same type, e.g., int[] numbers = {1, 2, 3, 4};
Nullable Types
C# supports nullable types, which allow value types to represent null in addition to their normal range of values. A nullable type is declared by appending a ? to the type.
int? number = null; // Nullable integer
bool? flag = true; // Nullable boolean
Example: Using Different Data Types
using System;
class Program
{
static void Main()
{
int age = 25;
string name = "John";
double height = 5.9;
bool isStudent = true;
Console.WriteLine("Name: " + name);
Console.WriteLine("Age: " + age);
Console.WriteLine("Height: " + height);
Console.WriteLine("Is student: " + isStudent);
}
}
C# Keywords
C# provides a set of reserved words known as "keywords". These keywords have predefined meanings and cannot be used for identifiers (like variable or function names). Keywords define the structure and flow of C# programs and are integral to the language syntax.
Common C# Keywords
Here's a list of some commonly used keywords in C# and their purposes:
- abstract: Defines an abstract class or an abstract method that must be implemented in a derived class.
- as: Used for type conversion between compatible types.
- base: Refers to the base class in a derived class.
- bool: Declares a Boolean type variable (true or false).
- break: Exits a loop or switch statement.
- case: Defines a branch in a switch statement.
- catch: Defines a block to catch exceptions in try-catch blocks.
- class: Defines a class in C#.
- const: Defines a constant value that cannot be changed once initialized.
- continue: Skips the current iteration in a loop and proceeds to the next iteration.
- decimal: Defines a decimal type for high-precision calculations.
- default: Specifies the default case in a switch statement.
- delegate: Defines a type that can reference methods with a particular parameter list and return type.
- do: Starts a do-while loop, which is guaranteed to run at least once.
- else: Specifies the block of code to execute if an
ifcondition is false. - enum: Defines an enumeration, a set of named constants.
- event: Declares an event that can be handled by event handlers in C#.
- explicit: Indicates that a type conversion requires a cast (usually for converting between incompatible types).
- extern: Declares an external method or function from another assembly or library.
- finally: Defines a block of code that is always executed after a try-catch block, even if an exception was thrown.
- for: Starts a for loop, which is used for iterating over a block of code a specified number of times.
- foreach: Iterates over a collection or array.
- goto: Transfers control to another part of the program. Typically avoided as it can make code harder to maintain.
- if: Executes a block of code based on a condition.
- implicit: Allows an implicit type conversion (done automatically by the compiler). The compiler does the conversion if possible.
- in: Defines parameters for iterating through collections or as a modifier for
refparameters that do not modify the argument. - interface: Defines a contract that classes must implement.
- internal: Limits the accessibility of types to within the same assembly.
- is: Tests whether an object is compatible with a given type.
- lock: Used for thread synchronization, ensuring that only one thread accesses a resource at a time.
- namespace: Declares a namespace, which is used for organizing code in C#.
- new: Creates a new instance of an object or hides a base class member.
- null: Represents a null reference, meaning no object is assigned.
- object: The base type from which all types (value types and reference types) are derived.
- override: Modifies a method in a derived class that was defined in a base class.
- params: Allows a method to accept a variable number of arguments.
- private: Specifies that a member is accessible only within the same class.
- protected: Specifies that a member is accessible within its class and by derived classes.
- public: Specifies that a member is accessible from anywhere.
- readonly: Declares a field that can only be assigned a value once (at declaration or in a constructor).
- ref: Passes a parameter by reference, meaning changes to the parameter will affect the argument passed in.
- return: Exits a method and optionally returns a value.
- sealed: Prevents a class from being inherited or prevents methods from being overridden in a derived class.
- sizeof: Returns the size, in bytes, of a type.
- static: Declares a class member that belongs to the class itself, rather than instances of the class.
- string: Represents a sequence of characters.
- struct: Defines a value type with fields and methods.
- switch: A control statement that evaluates a variable and branches based on its value.
- this: Refers to the current instance of a class or struct.
- throw: Used to throw an exception.
- try: Defines a block of code to attempt to run, typically followed by
catchorfinallyblocks. - void: Defines a method that does not return any value.
- while: Starts a while loop that continues as long as a condition is true.
Example of Using C# Keywords
using System;
class Program
{
// 'const' keyword is used to declare a constant
const int maxAge = 100;
static void Main()
{
// 'int' keyword defines an integer variable
int age = 25;
// 'if' keyword checks a condition
if (age <= maxAge)
{
Console.WriteLine("Age is within the allowed range.");
}
else
{
Console.WriteLine("Age exceeds the allowed range.");
}
}
}
In this example, several keywords are used:
- const for declaring a constant value.
- int for declaring an integer variable.
- if for performing a conditional check.
C# Conditional Statements
In C#, control statements are used to control the flow of execution of the program. These statements allow the program to make decisions and repeat actions based on conditions. Common control statements include if-else, switch, and loops like for.
C# if-else
The if-else statement is used to execute a block of code if a specified condition is true, and optionally execute another block of code if the condition is false.
using System;
class Program
{
static void Main()
{
int age = 20;
// Using if-else statement
if (age >= 18)
{
Console.WriteLine("You are eligible to vote.");
}
else
{
Console.WriteLine("You are not eligible to vote.");
}
}
}
In this example, if the value of age is greater than or equal to 18, it prints "You are eligible to vote." Otherwise, it prints "You are not eligible to vote."
C# switch
The switch statement evaluates an expression and executes the corresponding case block based on the value of the expression.
using System;
class Program
{
static void Main()
{
string day = "Monday";
// Using switch statement
switch (day)
{
case "Monday":
Console.WriteLine("Start of the week.");
break;
case "Friday":
Console.WriteLine("End of the week.");
break;
default:
Console.WriteLine("Midweek day.");
break;
}
}
}
C# Loops
for loop
A for loop repeats a block of code a specified number of times.
using System;
class Program
{
static void Main()
{
// Using for loop to print numbers from 1 to 5
for (int i = 1; i <= 5; i++)
{
Console.WriteLine(i);
}
}
}
while loop
A while loop repeats a block of code as long as the specified condition is true.
using System;
class Program
{
static void Main()
{
int i = 1;
// Using while loop
while (i <= 5)
{
Console.WriteLine(i);
i++;
}
}
}
do-while loop
A do-while loop is similar to a while loop, but guarantees that the block of code is executed at least once.
using System;
class Program
{
static void Main()
{
int i = 1;
// Using do-while loop
do
{
Console.WriteLine(i);
i++;
} while (i <= 5);
}
}
C# Functions
Functions (or methods) in C# allow you to organize your code into reusable blocks. These functions can receive input values, perform specific tasks, and return output values. In this section, we will explore different types of parameter passing techniques in C#: Call By Value, Call By Reference, and Out Parameters.
C# Call By Value
In Call By Value, when a function is called, the arguments are passed by value, meaning that a copy of the argument is passed to the function. Changes made to the parameter inside the function do not affect the original variable.
using System;
class Program
{
static void Main()
{
int x = 10;
Console.WriteLine("Before function call: " + x);
// Call by value
ChangeValue(x);
Console.WriteLine("After function call: " + x);
}
static void ChangeValue(int num)
{
num = 20; // Only modifies the local copy, not the original value
Console.WriteLine("Inside function: " + num);
}
}
In this example, the value of `x` remains unchanged after the `ChangeValue` function call, because the function works on a copy of `x`.
Call By Reference
In Call By Reference, the reference (or memory address) of the argument is passed to the function, which means that any changes made to the parameter inside the function will directly affect the original variable.
using System;
class Program
{
static void Main()
{
int x = 10;
Console.WriteLine("Before function call: " + x);
// Call by reference
ChangeValueRef(ref x);
Console.WriteLine("After function call: " + x);
}
static void ChangeValueRef(ref int num)
{
num = 20; // Modifies the original value, as it's passed by reference
Console.WriteLine("Inside function: " + num);
}
}
In this example, the value of `x` is changed to 20 after the function call because it was passed by reference using the `ref` keyword.
C# Out Parameter
The `out` keyword is used to pass a parameter to a method by reference, similar to `ref`. However, with `out`, the caller does not need to initialize the variable before passing it to the function, and the method must assign a value to the `out` parameter before returning.
using System;
class Program
{
static void Main()
{
int result;
bool success = Divide(10, 2, out result);
if(success)
{
Console.WriteLine("Division successful, result: " + result);
}
else
{
Console.WriteLine("Division failed");
}
}
static bool Divide(int numerator, int denominator, out int result)
{
if (denominator != 0)
{
result = numerator / denominator; // Assigns value to the out parameter
return true;
}
else
{
result = 0; // Must assign a value before returning
return false;
}
}
}
In this example, the `Divide` method uses an `out` parameter to return the result of the division. The caller doesn't need to initialize the `result` variable before calling the function. The method assigns a value to it within the function.
C# Arrays
Arrays in C# allow you to store multiple values in a single variable. They are particularly useful when you need to work with a collection of similar items, such as numbers, strings, or objects. C# provides various types of arrays, including single-dimensional and multi-dimensional arrays.
C# Array Declaration and Initialization
In C#, an array is a fixed-size data structure that can store elements of the same data type. Arrays are zero-indexed, meaning the first element is accessed at index 0.
using System;
class Program
{
static void Main()
{
// Single-dimensional array
int[] numbers = new int[] { 1, 2, 3, 4, 5 };
Console.WriteLine("First number: " + numbers[0]); // Accessing the first element
// Array length
Console.WriteLine("Array length: " + numbers.Length);
}
}
The above example demonstrates how to declare and initialize a single-dimensional array in C#. We access the elements using an index and use the `Length` property to get the size of the array.
C# Array to Function
You can pass an array as a parameter to a function, allowing the function to manipulate the array directly. Since arrays in C# are reference types, the function can modify the array content.
using System;
class Program
{
static void Main()
{
int[] numbers = { 1, 2, 3, 4, 5 };
Console.WriteLine("Before function call:");
foreach (var number in numbers)
{
Console.Write(number + " ");
}
// Passing array to function
ModifyArray(numbers);
Console.WriteLine("\nAfter function call:");
foreach (var number in numbers)
{
Console.Write(number + " ");
}
}
static void ModifyArray(int[] arr)
{
// Modify array elements
for (int i = 0; i < arr.Length; i++)
{
arr[i] = arr[i] * 2; // Doubling each element
}
}
}
In this example, the array `numbers` is passed to the `ModifyArray` function. Inside the function, we modify each element of the array by multiplying it by 2. Since arrays are reference types, the changes made inside the function affect the original array.
C# Multidimensional Array
A multidimensional array allows you to store data in multiple dimensions. The most common types are two-dimensional arrays (like a matrix) and three-dimensional arrays.
using System;
class Program
{
static void Main()
{
// 2D array (matrix)
int[,] matrix = new int[2, 3] { { 1, 2, 3 }, { 4, 5, 6 } };
// Accessing elements of the 2D array
Console.WriteLine("Element at [0, 1]: " + matrix[0, 1]); // Outputs 2
// Iterate through the 2D array
for (int i = 0; i < matrix.GetLength(0); i++)
{
for (int j = 0; j < matrix.GetLength(1); j++)
{
Console.Write(matrix[i, j] + " ");
}
Console.WriteLine();
}
}
}
The above example demonstrates a two-dimensional array, `matrix`, where we have 2 rows and 3 columns. The `GetLength` method is used to determine the number of rows and columns. We also use a nested loop to iterate over the elements in the 2D array.
using System;
class Program
{
static void Main()
{
// 3D array
int[,,] cube = new int[2, 2, 2]
{
{ {1, 2}, {3, 4} },
{ {5, 6}, {7, 8} }
};
// Accessing elements of the 3D array
Console.WriteLine("Element at [0, 1, 1]: " + cube[0, 1, 1]); // Outputs 4
}
}
In this example, we created a 3D array called `cube`. A three-dimensional array is useful for storing data with multiple layers, such as a Rubik's cube or three-dimensional spatial data.
C# Object and Class
In C#, an object is an instance of a class. A class is a blueprint for creating objects, and it defines the properties and methods that the objects will have. The `Object` class is the root of the class hierarchy, and every class in C# directly or indirectly derives from it.
Creating an Object
To create an object of a class in C#, you use the `new` keyword, followed by the class name. This instantiates an object of the class, allowing you to use its properties and methods.
using System;
class Car
{
public string Model { get; set; }
public int Year { get; set; }
public void Drive()
{
Console.WriteLine($"Driving the {Model} car of year {Year}.");
}
}
class Program
{
static void Main()
{
// Create an object of Car class
Car myCar = new Car { Model = "Tesla", Year = 2022 };
// Access the object properties
Console.WriteLine($"Car Model: {myCar.Model}, Year: {myCar.Year}");
// Call the method of the object
myCar.Drive(); // Output: Driving the Tesla car of year 2022.
}
}
In this example, we define a `Car` class with properties (`Model` and `Year`) and a method (`Drive`). We then create an object of the `Car` class using the `new` keyword and access its properties and methods.
C# Constructor
A constructor is a special method that is automatically called when an object of a class is created. It is used to initialize the object's state. Constructors do not have a return type, and their name must be the same as the class name. Constructors can be overloaded to accept parameters.
1. Default Constructor
A default constructor is provided by C# if no other constructors are defined. It initializes the object with default values.
using System;
class Employee
{
public string Name { get; set; }
public int Age { get; set; }
// Default constructor
public Employee()
{
Name = "Unknown";
Age = 0;
}
}
class Program
{
static void Main()
{
Employee emp = new Employee();
Console.WriteLine($"Employee Name: {emp.Name}, Age: {emp.Age}"); // Output: Employee Name: Unknown, Age: 0
}
}
In this example, we define a default constructor for the `Employee` class. When we create an `Employee` object, the constructor sets the default values for the `Name` and `Age` properties.
2. Parameterized Constructor
A parameterized constructor is one that accepts arguments when creating an object. It allows you to initialize an object with specific values at the time of creation.
using System;
class Student
{
public string Name { get; set; }
public int RollNumber { get; set; }
// Parameterized constructor
public Student(string name, int rollNumber)
{
Name = name;
RollNumber = rollNumber;
}
}
class Program
{
static void Main()
{
Student student = new Student("Alice", 101);
Console.WriteLine($"Student Name: {student.Name}, Roll Number: {student.RollNumber}"); // Output: Student Name: Alice, Roll Number: 101
}
}
In this example, we use a parameterized constructor to initialize the `Student` object with specific values when it is created.
C# Destructor
A destructor is a special method that is called when an object is destroyed or goes out of scope. It is used to perform cleanup operations, such as releasing unmanaged resources. The destructor's name is the same as the class name but with a tilde (~) prefix. C# provides automatic garbage collection, so destructors are rarely needed unless working with unmanaged resources.
Destructor Example
Here's an example of a destructor that cleans up resources when an object is destroyed:
using System;
class DatabaseConnection
{
public string ConnectionString { get; set; }
public DatabaseConnection(string connectionString)
{
ConnectionString = connectionString;
Console.WriteLine("Database connection opened.");
}
// Destructor
~DatabaseConnection()
{
Console.WriteLine("Database connection closed.");
}
}
class Program
{
static void Main()
{
// Creating an object of DatabaseConnection
DatabaseConnection dbConn = new DatabaseConnection("Server=myServer;Database=myDB;");
}
}
In this example, we define a destructor in the `DatabaseConnection` class. The destructor is automatically called when the object goes out of scope, and it outputs a message indicating that the connection is closed. The destructor is called by the garbage collector, which is responsible for releasing the resources.
C# this
In C#, the `this` keyword refers to the current instance of the class. It is primarily used to distinguish between instance variables and parameters when they have the same name. It can also be used to pass the current object as a parameter to another method or to call another constructor in the same class (constructor chaining).
Using this to Reference Instance Variables
In the case where a parameter has the same name as a class member, you can use `this` to refer to the class member.
using System;
class Student
{
public string Name;
public int Age;
// Constructor with parameters
public Student(string Name, int Age)
{
this.Name = Name; // 'this.Name' refers to the class member, 'Name' refers to the parameter
this.Age = Age; // 'this.Age' refers to the class member, 'Age' refers to the parameter
}
public void Display()
{
Console.WriteLine($"Name: {this.Name}, Age: {this.Age}");
}
}
class Program
{
static void Main()
{
// Create an object of Student class
Student student = new Student("John", 21);
student.Display();
}
}
In this example, the `Student` class has a constructor where both the instance variables (`Name` and `Age`) and the parameters have the same name. The `this` keyword is used to differentiate the class variables from the method parameters.
C# static
The `static` keyword is used to define members (variables, methods, classes) that belong to the class itself rather than to any specific instance of the class. Static members can be accessed without creating an instance of the class. It is used when you want to share data across all instances of the class or to define utility methods.
1. Static Variables
Static variables are shared across all instances of a class. They are initialized only once and retain their value across different object instances.
using System;
class Counter
{
public static int Count = 0;
// Constructor increments static Count variable
public Counter()
{
Count++;
}
}
class Program
{
static void Main()
{
Counter c1 = new Counter();
Counter c2 = new Counter();
Console.WriteLine($"Count: {Counter.Count}"); // Output: Count: 2
}
}
In this example, the `Count` variable is declared as `static`, meaning it is shared among all instances of the `Counter` class. Each time a `Counter` object is created, the `Count` variable is incremented, and the final value of `Count` is shared across all instances.
2. Static Methods
Static methods can be called without creating an instance of the class. They can only access static members of the class.
using System;
class Calculator
{
public static int Add(int a, int b)
{
return a + b;
}
}
class Program
{
static void Main()
{
int sum = Calculator.Add(5, 3);
Console.WriteLine($"Sum: {sum}"); // Output: Sum: 8
}
}
Here, the `Add` method is static, meaning it can be called directly from the class without the need for an instance. It performs the addition of two numbers and returns the result.
C# static class
A static class is a class that cannot be instantiated. It can only contain static members (fields, methods, properties, etc.). Static classes are typically used to group related utility functions or data that don't require object instantiation. The System.Math class is a good example of a static class.
Defining a Static Class
Static classes are useful for methods that operate only on the arguments passed to them and do not need to maintain any internal state.
using System;
public static class MathUtilities
{
// Static method in a static class
public static int Add(int a, int b)
{
return a + b;
}
public static int Multiply(int a, int b)
{
return a * b;
}
}
class Program
{
static void Main()
{
int sum = MathUtilities.Add(5, 3);
int product = MathUtilities.Multiply(5, 3);
Console.WriteLine($"Sum: {sum}, Product: {product}");
}
}
In this example, the MathUtilities class is defined as a static class. The methods Add and Multiply can be called directly from the class without creating an instance of it. Static classes are useful for grouping related utility functions, like mathematical operations, that don't depend on object state.
C# Static Constructor
A static constructor in C# is used to initialize static members of a class or perform any other static initialization tasks. It is called automatically before any static members or methods of the class are accessed. The static constructor doesn't take any parameters and is called only once, even if multiple instances of the class are created.
Static Constructor Example
The static constructor is used to initialize static fields when the class is first accessed.
using System;
class Car
{
public static int TotalCars;
public string Model;
// Static constructor
static Car()
{
TotalCars = 0;
Console.WriteLine("Static constructor called!");
}
// Instance constructor
public Car(string model)
{
Model = model;
TotalCars++;
}
public void DisplayInfo()
{
Console.WriteLine($"Model: {Model}, Total Cars: {TotalCars}");
}
}
class Program
{
static void Main()
{
Car car1 = new Car("Toyota");
car1.DisplayInfo(); // Output: Model: Toyota, Total Cars: 1
Car car2 = new Car("Honda");
car2.DisplayInfo(); // Output: Model: Honda, Total Cars: 2
}
}
In this example, the static constructor is used to initialize the static TotalCars field to 0. The static constructor is called automatically before any instance is created or any static member is accessed.
C# Structs
A struct in C# is a value type that represents a structure consisting of a set of related variables. Structs are similar to classes, but they are value types and do not support inheritance. They are typically used to represent small, lightweight objects like points, rectangles, and other simple structures.
Struct Definition and Example
Structs are often used when you need a lightweight object that does not need to inherit from a class. Unlike classes, structs are copied by value, meaning when a struct is assigned to another struct, a copy of its value is made.
using System;
struct Point
{
public int X;
public int Y;
// Constructor
public Point(int x, int y)
{
X = x;
Y = y;
}
// Method to display point info
public void DisplayPoint()
{
Console.WriteLine($"Point: ({X}, {Y})");
}
}
class Program
{
static void Main()
{
Point point1 = new Point(10, 20);
point1.DisplayPoint(); // Output: Point: (10, 20)
Point point2 = point1; // Copying value of point1
point2.X = 30; // Changing point2 value
point2.DisplayPoint(); // Output: Point: (30, 20)
point1.DisplayPoint(); // Output: Point: (10, 20) - point1 remains unaffected
}
}
In this example, Point is a struct with fields X and Y. We create two Point structs, and when we assign point1 to point2, a copy of the value is made. This demonstrates the value type behavior of structs in C#.
C# Enum
An enum (short for "enumeration") in C# is a special data type that represents a set of named constants. Enums are useful when you need to represent a collection of related constants, such as days of the week, months of the year, or states of a system. Enums are backed by integral types (e.g., int) by default, but you can specify a different underlying type.
Enum Definition and Example
Enums make the code more readable and maintainable by using meaningful names instead of numeric values. You can also explicitly set values for the enum members.
using System;
enum Days
{
Sunday = 1,
Monday,
Tuesday,
Wednesday,
Thursday,
Friday,
Saturday
}
class Program
{
static void Main()
{
Days today = Days.Monday;
Console.WriteLine($"Today is {today}"); // Output: Today is Monday
// Convert Enum to integer
int dayValue = (int)today;
Console.WriteLine($"Day value: {dayValue}"); // Output: Day value: 2
// Convert integer to Enum
Days day = (Days)3;
Console.WriteLine($"Day 3 is {day}"); // Output: Day 3 is Wednesday
}
}
In this example, we define an enum Days to represent the days of the week. We assign explicit integer values starting from 1 for Sunday. Enums make it easy to work with sets of related constants and improve code readability.
C# Inheritance
Inheritance in C# is a fundamental Object-Oriented Programming concept that allows one class to inherit the properties and methods of another class. The class that is inherited is called the base class (or parent class), and the class that inherits is called the derived class (or child class). This enables code reuse and establishes a relationship between classes.
Basic Inheritance Example
In the following example, Animal is the base class and Dog is the derived class. The Dog class inherits the properties and methods of Animal.
using System;
// Base Class
class Animal
{
public string Name { get; set; }
public void Eat()
{
Console.WriteLine($"{Name} is eating.");
}
}
// Derived Class
class Dog : Animal
{
public void Bark()
{
Console.WriteLine($"{Name} is barking!");
}
}
class Program
{
static void Main()
{
Dog dog = new Dog();
dog.Name = "Buddy";
dog.Eat(); // Output: Buddy is eating.
dog.Bark(); // Output: Buddy is barking!
}
}
In this example, the Dog class inherits the Name property and Eat method from the Animal class. Additionally, it introduces a new method called Bark.
Overriding Methods
You can override methods in the derived class to provide a specialized implementation of a method defined in the base class. This is done using the virtual keyword in the base class and the override keyword in the derived class.
using System;
// Base Class
class Animal
{
public virtual void Speak()
{
Console.WriteLine("Animal sound.");
}
}
// Derived Class
class Dog : Animal
{
public override void Speak()
{
Console.WriteLine("Dog barks.");
}
}
class Program
{
static void Main()
{
Animal myAnimal = new Animal();
myAnimal.Speak(); // Output: Animal sound.
Dog myDog = new Dog();
myDog.Speak(); // Output: Dog barks.
}
}
In this example, the Speak method in the Animal class is marked as virtual, allowing it to be overridden in the Dog class. The Dog class provides a custom implementation of Speak.
C# Aggregation
Aggregation is a relationship where one class is a part of another class but can exist independently. It represents a has-a relationship, where an object is a member of another object. Aggregation is different from inheritance because the associated objects are not dependent on the lifecycle of the parent object.
Basic Aggregation Example
In this example, a Library contains a collection of Book objects. A Book can exist independently of a Library, making it a perfect case for aggregation.
using System;
using System.Collections.Generic;
// Class representing a Book
class Book
{
public string Title { get; set; }
public string Author { get; set; }
public void Display()
{
Console.WriteLine($"Title: {Title}, Author: {Author}");
}
}
// Class representing a Library (Aggregation)
class Library
{
public List Books { get; set; }
public Library()
{
Books = new List();
}
public void AddBook(Book book)
{
Books.Add(book);
}
public void DisplayBooks()
{
Console.WriteLine("Books in the library:");
foreach (var book in Books)
{
book.Display();
}
}
}
class Program
{
static void Main()
{
Book book1 = new Book { Title = "1984", Author = "George Orwell" };
Book book2 = new Book { Title = "Brave New World", Author = "Aldous Huxley" };
Library library = new Library();
library.AddBook(book1);
library.AddBook(book2);
library.DisplayBooks();
}
}
In this example, Library has a list of Book objects. Each Book is created independently and added to the Library. Even if the Library is destroyed, the Book objects still exist.
C# Polymorphism
Polymorphism in C# is a core concept of Object-Oriented Programming that allows objects to be treated as instances of their parent class while still calling methods that are specific to the derived class. Polymorphism is primarily achieved through Method Overriding and Member Overloading.
Compile-Time vs. Run-Time Polymorphism
- Compile-Time Polymorphism: Achieved through method overloading and operator overloading. It's resolved during compilation.
- Run-Time Polymorphism: Achieved through method overriding using inheritance and is resolved during runtime.
C# Member Overloading
Member Overloading allows multiple methods with the same name but different parameters (type, number, or both) in the same class. It is an example of Compile-Time Polymorphism.
Example of Method Overloading
using System;
class Calculator
{
// Overloaded method with two parameters
public int Add(int a, int b)
{
return a + b;
}
// Overloaded method with three parameters
public int Add(int a, int b, int c)
{
return a + b + c;
}
}
class Program
{
static void Main()
{
Calculator calc = new Calculator();
Console.WriteLine(calc.Add(2, 3)); // Output: 5
Console.WriteLine(calc.Add(2, 3, 4)); // Output: 9
}
}
In this example, the Add method is overloaded with two versions: one accepting two parameters and another accepting three. The appropriate method is selected based on the arguments passed.
C# Method Overriding
Method Overriding allows a derived class to provide a specific implementation of a method that is already defined in its base class. It is an example of Run-Time Polymorphism. The base class method must be marked with the virtual keyword, and the derived class method with the override keyword.
Example of Method Overriding
using System;
class Animal
{
// Base class method marked as virtual
public virtual void Speak()
{
Console.WriteLine("Animal makes a sound.");
}
}
class Dog : Animal
{
// Overridden method in the derived class
public override void Speak()
{
Console.WriteLine("Dog barks.");
}
}
class Program
{
static void Main()
{
Animal myAnimal = new Animal();
myAnimal.Speak(); // Output: Animal makes a sound.
Animal myDog = new Dog();
myDog.Speak(); // Output: Dog barks.
}
}
In this example, the Speak method in the Dog class overrides the Speak method of the Animal class, allowing Dog instances to provide a specialized implementation.
C# Base
The base keyword in C# is used within a derived class to refer to the members of its base class. It is commonly used to:
- Access a method or property from the base class that has been overridden.
- Invoke a constructor from the base class.
Example of Using base Keyword
using System;
class Person
{
public string Name { get; set; }
public Person(string name)
{
Name = name;
}
public virtual void ShowInfo()
{
Console.WriteLine($"Name: {Name}");
}
}
class Employee : Person
{
public int EmployeeID { get; set; }
// Constructor using base keyword to call the base class constructor
public Employee(string name, int id) : base(name)
{
EmployeeID = id;
}
// Overridden method using base to access base class method
public override void ShowInfo()
{
base.ShowInfo();
Console.WriteLine($"Employee ID: {EmployeeID}");
}
}
class Program
{
static void Main()
{
Employee emp = new Employee("John Doe", 1234);
emp.ShowInfo();
}
}
In this example, the base keyword is used to invoke the constructor of the base class Person and also to call the ShowInfo method from the base class within the overridden method in Employee.
C# Sealed
The `sealed` keyword in C# is used to prevent a class from being inherited or to prevent an overridden method from being further overridden. A sealed class cannot act as a base class.
Example of Sealed Class
using System;
// Sealed Class
sealed class FinalClass
{
public void Display()
{
Console.WriteLine("This is a sealed class.");
}
}
// Trying to inherit from a sealed class will result in a compilation error
// class DerivedClass : FinalClass {} // Error: Cannot derive from sealed class
class Program
{
static void Main()
{
FinalClass finalObj = new FinalClass();
finalObj.Display(); // Output: This is a sealed class.
}
}
In this example, the FinalClass is sealed, meaning it cannot be inherited. Any attempt to create a derived class from FinalClass will result in a compilation error.
Example of Sealed Method
using System;
class Animal
{
// Virtual method in the base class
public virtual void Move()
{
Console.WriteLine("Animal moves.");
}
}
class Bird : Animal
{
// Overridden method in the derived class
public override void Move()
{
Console.WriteLine("Bird flies.");
}
// Sealed method in the derived class
public sealed override void Speak()
{
Console.WriteLine("Bird chirps.");
}
}
// Further derived class
class Sparrow : Bird
{
// Cannot override Speak() because it is sealed in the Bird class
// public override void Speak() {} // Error: 'Bird.Speak()' cannot be overridden because it is sealed
}
class Program
{
static void Main()
{
Bird bird = new Bird();
bird.Speak(); // Output: Bird chirps.
}
}
In this example, the Speak method in the Bird class is sealed, preventing further derived classes (like Sparrow) from overriding it.
C# Abstraction
An Abstract Class is a class that cannot be instantiated. It is meant to be a base class for other classes. It can contain abstract methods (methods without a body) and concrete methods (methods with a body). The derived class must provide an implementation for all abstract methods.
Key Points about Abstract Classes
- An abstract class can have both abstract and non-abstract methods.
- It cannot be instantiated directly.
- Abstract methods must be overridden in derived classes.
Example of Abstract Class
using System;
// Abstract Class
abstract class Shape
{
// Abstract Method (no body)
public abstract void Draw();
// Non-abstract Method
public void Display()
{
Console.WriteLine("Displaying shape details.");
}
}
// Derived Class
class Circle : Shape
{
// Providing implementation of the abstract method
public override void Draw()
{
Console.WriteLine("Drawing a circle.");
}
}
class Program
{
static void Main()
{
// Shape shape = new Shape(); // Error: Cannot instantiate an abstract class
Shape circle = new Circle();
circle.Draw(); // Output: Drawing a circle.
circle.Display(); // Output: Displaying shape details.
}
}
In this example, the Shape class is abstract with an abstract method Draw and a non-abstract method Display. The Circle class inherits from Shape and provides a concrete implementation of the Draw method.
C# Interface
An Interface in C# is a contract that defines a set of methods, properties, events, or indexers without implementing them. A class or struct that implements an interface must implement all its members. Interfaces are purely abstract, meaning they cannot contain any implementation.
Key Points about Interfaces
- An interface can only contain declarations, not implementations.
- A class or struct can implement multiple interfaces, enabling multiple inheritance.
- All members of an interface are public by default.
Example of Interface
using System;
// Interface definition
interface IShape
{
// Method signatures without implementation
void Draw();
double GetArea();
}
// Class implementing the interface
class Rectangle : IShape
{
public double Width { get; set; }
public double Height { get; set; }
public Rectangle(double width, double height)
{
Width = width;
Height = height;
}
// Implementing the methods from the interface
public void Draw()
{
Console.WriteLine("Drawing a rectangle.");
}
public double GetArea()
{
return Width * Height;
}
}
class Program
{
static void Main()
{
IShape rectangle = new Rectangle(5.0, 10.0);
rectangle.Draw(); // Output: Drawing a rectangle.
Console.WriteLine($"Area: {rectangle.GetArea()}"); // Output: Area: 50
}
}
In this example, the IShape interface is implemented by the Rectangle class. The Rectangle class must provide an implementation for the Draw and GetArea methods defined in the interface.
Differences Between Abstract Classes and Interfaces
| Aspect | Abstract Class | Interface |
|---|---|---|
| Implementation | Can have both abstract (unimplemented) and concrete (implemented) methods. | Only contains declarations; no implementation allowed. |
| Instantiation | Cannot be instantiated directly. | Cannot be instantiated directly. |
| Inheritance | A class can inherit from only one abstract class. | A class can implement multiple interfaces. |
| Access Modifiers | Can have access modifiers (e.g., private, protected). | All members are implicitly public and cannot have access modifiers. |
| Fields | Can have fields, properties, and constructors. | Cannot have fields or constructors, only properties, methods, events, or indexers. |
C# Namespaces
A Namespace in C# is used to organize classes, structs, interfaces, enums, and delegates. It is a way of logically grouping similar classes to avoid naming conflicts. Think of a namespace as a container for a set of related classes.
Why Use Namespaces?
- Helps in avoiding naming conflicts between classes.
- Makes it easier to organize and manage large projects.
- Allows for better code readability and maintenance.
Example of Namespaces
using System;
namespace ProjectA
{
// Class within namespace ProjectA
class ExampleClass
{
public void Display()
{
Console.WriteLine("This is ProjectA.ExampleClass.");
}
}
}
namespace ProjectB
{
// Class within namespace ProjectB
class ExampleClass
{
public void Display()
{
Console.WriteLine("This is ProjectB.ExampleClass.");
}
}
}
class Program
{
static void Main()
{
// Use fully qualified names to avoid conflict
ProjectA.ExampleClass objA = new ProjectA.ExampleClass();
ProjectB.ExampleClass objB = new ProjectB.ExampleClass();
objA.Display(); // Output: This is ProjectA.ExampleClass.
objB.Display(); // Output: This is ProjectB.ExampleClass.
}
}
In this example, two classes named `ExampleClass` are created in different namespaces, `ProjectA` and `ProjectB`. Using namespaces allows them to coexist without naming conflicts.
C# Access Modifiers
Access Modifiers in C# are keywords used to specify the accessibility level of classes and members. They control the visibility of code to other parts of the program, providing a way to implement encapsulation.
Types of Access Modifiers
- public - No restrictions; accessible from anywhere.
- private - Accessible only within the containing class.
- protected - Accessible within the containing class and derived classes.
- internal - Accessible within the same assembly but not from another assembly.
- protected internal - Accessible within the same assembly and in derived classes.
- private protected - Accessible within the containing class or types derived from the containing class within the same assembly.
Example of Access Modifiers
using System;
class Person
{
// Public - accessible from anywhere
public string Name { get; set; }
// Private - accessible only within the Person class
private int age;
// Protected - accessible in derived classes
protected string Gender { get; set; }
// Internal - accessible within the same assembly
internal string Nationality { get; set; }
// Constructor
public Person(string name, int age, string gender)
{
Name = name;
this.age = age;
Gender = gender;
}
// Method to display information
public void DisplayInfo()
{
Console.WriteLine($"Name: {Name}, Age: {age}, Gender: {Gender}");
}
}
class Employee : Person
{
public Employee(string name, int age, string gender)
: base(name, age, gender)
{
}
public void DisplayEmployeeGender()
{
Console.WriteLine($"Gender (from derived class): {Gender}");
}
}
class Program
{
static void Main()
{
Person person = new Person("Alice", 30, "Female");
person.DisplayInfo(); // Output: Name: Alice, Age: 30, Gender: Female
Employee employee = new Employee("Bob", 40, "Male");
employee.DisplayEmployeeGender(); // Output: Gender (from derived class): Male
}
}
In this example, various access modifiers are used within the `Person` class, demonstrating how they restrict or allow access to class members. The `Employee` class, which inherits from `Person`, can access `protected` members.
C# Encapsulation
Encapsulation is the practice of bundling data (variables) and code (methods) that operates on the data into a single unit or class. It restricts direct access to some components, which is a fundamental concept in object-oriented programming. Encapsulation is implemented using access modifiers in C#.
Benefits of Encapsulation
- Protects the internal state of an object from unintended modification.
- Encourages modularity and easy maintenance of code.
- Allows changes to the implementation without affecting other parts of the code.
Example of Encapsulation
using System;
class BankAccount
{
// Private field (Encapsulation)
private double balance;
// Public property with validation
public double Balance
{
get { return balance; }
set
{
if (value >= 0)
balance = value;
else
Console.WriteLine("Balance cannot be negative.");
}
}
// Constructor
public BankAccount(double initialBalance)
{
Balance = initialBalance;
}
// Method to deposit money
public void Deposit(double amount)
{
if (amount > 0)
{
Balance += amount;
Console.WriteLine($"Deposited: {amount}. New Balance: {Balance}");
}
else
{
Console.WriteLine("Deposit amount must be positive.");
}
}
// Method to withdraw money
public void Withdraw(double amount)
{
if (amount > 0 && amount <= Balance)
{
Balance -= amount;
Console.WriteLine($"Withdrawn: {amount}. New Balance: {Balance}");
}
else
{
Console.WriteLine("Invalid withdrawal amount.");
}
}
}
class Program
{
static void Main()
{
BankAccount account = new BankAccount(1000);
account.Deposit(500); // Output: Deposited: 500. New Balance: 1500
account.Withdraw(300); // Output: Withdrawn: 300. New Balance: 1200
account.Balance = -200; // Output: Balance cannot be negative.
}
}
In this example, the `BankAccount` class encapsulates the `balance` field and provides controlled access through the `Balance` property. It ensures that negative balances are not set, enforcing proper behavior.
C# Exception Handling
Exception Handling in C# is a mechanism for handling runtime errors, making programs more robust and preventing unexpected crashes. It allows you to catch errors, handle them gracefully, and maintain the flow of the application. Exception handling in C# is achieved primarily through the use of the try, catch, and finally blocks.
Commonly Used Exception Handling Classes
- Exception - Base class for all exceptions.
- SystemException - Base class for system-related exceptions.
- ApplicationException - Base class for application-related exceptions.
- NullReferenceException - Thrown when there is an attempt to use an object reference that is not set to an instance of an object.
- IndexOutOfRangeException - Thrown when attempting to access an index that is outside the bounds of an array.
- DivideByZeroException - Thrown when dividing a number by zero.
- InvalidOperationException - Thrown when a method call is invalid for the object's current state.
C# try/catch
The try block in C# is used to wrap code that might throw an exception. If an exception occurs, it is caught by the catch block, which handles the exception. You can have multiple catch blocks to handle different types of exceptions specifically.
Syntax of try/catch
try
{
// Code that may throw an exception
}
catch (ExceptionType ex)
{
// Code to handle the exception
}
Example of try/catch
using System;
class Program
{
static void Main()
{
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // This will throw an IndexOutOfRangeException
}
catch (IndexOutOfRangeException ex)
{
Console.WriteLine("Error: Attempted to access an index outside the bounds of the array.");
}
catch (Exception ex)
{
// General exception handler
Console.WriteLine($"General Error: {ex.Message}");
}
}
}
In this example, the code attempts to access an invalid array index, triggering an IndexOutOfRangeException. The catch block catches the exception and handles it gracefully, preventing the application from crashing.
C# finally
The finally block in C# is used to execute code after the try and catch blocks, regardless of whether an exception was thrown or not. It is commonly used to release resources like file handles, database connections, or any cleanup code that must always execute.
Syntax of try/catch/finally
try
{
// Code that may throw an exception
}
catch (ExceptionType ex)
{
// Code to handle the exception
}
finally
{
// Code that will always execute
}
Example of try/catch/finally
using System;
using System.IO;
class Program
{
static void Main()
{
StreamReader reader = null;
try
{
reader = new StreamReader("example.txt");
string content = reader.ReadToEnd();
Console.WriteLine(content);
}
catch (FileNotFoundException ex)
{
Console.WriteLine("Error: File not found.");
}
catch (Exception ex)
{
// General exception handler
Console.WriteLine($"General Error: {ex.Message}");
}
finally
{
// Ensure the reader is always closed
if (reader != null)
{
reader.Close();
Console.WriteLine("File stream closed.");
}
}
}
}
In this example, the finally block ensures that the file stream is closed, even if an exception occurs while reading the file. This guarantees resource cleanup.
C# Custom Exceptions
Custom Exceptions in C# are user-defined exceptions that allow you to create meaningful and specific error handling scenarios for your applications. Creating a custom exception involves deriving a new class from the built-in Exception class.
Creating a Custom Exception
using System;
class InvalidAgeException : Exception
{
public InvalidAgeException(string message) : base(message)
{
}
}
class Program
{
static void Main()
{
try
{
ValidateAge(15);
}
catch (InvalidAgeException ex)
{
Console.WriteLine($"Custom Exception Caught: {ex.Message}");
}
}
static void ValidateAge(int age)
{
if (age < 18)
{
throw new InvalidAgeException("Age must be 18 or older.");
}
}
}
In this example, a custom exception called InvalidAgeException is created, which inherits from the Exception class. It is thrown when the ValidateAge method detects an invalid age.
C# checked and unchecked
checked and unchecked keywords in C# are used to control the overflow behavior in arithmetic operations. By default, C# does not check for arithmetic overflow unless explicitly specified. Using checked, you can force C# to throw an OverflowException when overflow occurs. unchecked ignores overflow.
Example of checked
using System;
class Program
{
static void Main()
{
try
{
int maxValue = int.MaxValue;
int result = checked(maxValue + 1); // This will throw an OverflowException
}
catch (OverflowException ex)
{
Console.WriteLine("Overflow detected using 'checked': " + ex.Message);
}
}
}
In this example, the checked block detects overflow when adding to the maximum integer value, resulting in an OverflowException.
Example of unchecked
using System;
class Program
{
static void Main()
{
int maxValue = int.MaxValue;
int result = unchecked(maxValue + 1); // No exception thrown, overflow ignored
Console.WriteLine("Unchecked result: " + result);
}
}
Here, the unchecked keyword allows the overflow to occur without throwing an exception. The result wraps around to the negative range.
C# SystemException
SystemException is the base class for all system-related exceptions in C#. It derives from the Exception class and includes common runtime exceptions like NullReferenceException, IndexOutOfRangeException, and DivideByZeroException. These exceptions generally indicate a problem with the code logic or system errors.
Common SystemException Examples
- NullReferenceException - Occurs when trying to access a member on a null object reference.
- IndexOutOfRangeException - Thrown when accessing an array index that is out of bounds.
- DivideByZeroException - Thrown when dividing by zero.
Example of SystemException
using System;
class Program
{
static void Main()
{
try
{
int[] numbers = { 1, 2, 3 };
Console.WriteLine(numbers[5]); // This will throw an IndexOutOfRangeException
}
catch (SystemException ex)
{
Console.WriteLine("System Exception Caught: " + ex.Message);
}
}
}
This example catches a SystemException, which includes an IndexOutOfRangeException since it inherits from SystemException. Using SystemException can catch a broader range of system-related errors, but it's usually better to handle exceptions more specifically.
C# File IO (Input/Output)
In C#, File IO (Input/Output) refers to the process of reading from and writing to files. C# provides a set of classes for handling file operations, including FileStream, StreamWriter, and StreamReader. These classes are located in the System.IO namespace and allow you to perform operations like creating, reading, writing, and deleting files.
C# FileStream
FileStream is a class in C# used to read from and write to files as a stream of bytes. It is useful for handling binary data and allows both synchronous and asynchronous operations. A FileStream can be opened in various modes like Open, Create, Append, and Truncate.
FileStream Example - Writing to a File
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "example.txt";
// Using FileStream to write data
using (FileStream fileStream = new FileStream(filePath, FileMode.Create))
{
byte[] data = System.Text.Encoding.UTF8.GetBytes("Hello, FileStream!");
fileStream.Write(data, 0, data.Length);
Console.WriteLine("Data written to file using FileStream.");
}
}
}
In this example, FileStream is used to write a string to a file called example.txt. The FileMode.Create mode is used to create a new file or overwrite an existing one.
FileStream Example - Reading from a File
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "example.txt";
// Using FileStream to read data
using (FileStream fileStream = new FileStream(filePath, FileMode.Open))
{
byte[] data = new byte[fileStream.Length];
fileStream.Read(data, 0, data.Length);
string content = System.Text.Encoding.UTF8.GetString(data);
Console.WriteLine("Data read from file using FileStream: " + content);
}
}
}
Here, FileStream is used to read the contents of example.txt. The data is read as bytes and then converted to a string using UTF-8 encoding.
C# StreamWriter
StreamWriter is a class used for writing characters to a stream. It is typically used for writing text files and is easier to use than FileStream for text data. StreamWriter supports writing strings, characters, and arrays of characters.
StreamWriter Example
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "example.txt";
// Using StreamWriter to write text
using (StreamWriter writer = new StreamWriter(filePath))
{
writer.WriteLine("Hello, StreamWriter!");
writer.WriteLine("Writing to a file is easy with StreamWriter.");
Console.WriteLine("Data written to file using StreamWriter.");
}
}
}
In this example, StreamWriter is used to write multiple lines of text to example.txt. The StreamWriter class provides a convenient way to write text data with a simpler syntax compared to FileStream.
C# StreamReader
StreamReader is a class used for reading characters from a stream, making it ideal for reading text files. It provides methods for reading individual characters, lines, or the entire content of a file as a string.
StreamReader Example
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "example.txt";
// Using StreamReader to read text
using (StreamReader reader = new StreamReader(filePath))
{
string content = reader.ReadToEnd();
Console.WriteLine("Data read from file using StreamReader: ");
Console.WriteLine(content);
}
}
}
This example shows how to read the entire content of example.txt using StreamReader. The ReadToEnd method reads all characters from the current position to the end of the stream.
C# TextWriter
TextWriter is an abstract class in C# used to write a series of characters to a stream. It serves as the base class for classes like StreamWriter and StringWriter. You can use it to write text to different sources, including files and strings.
TextWriter Example - Writing to a File
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "textwriter_example.txt";
// Using TextWriter to write data
using (TextWriter writer = File.CreateText(filePath))
{
writer.WriteLine("Hello, TextWriter!");
writer.WriteLine("Writing to a file using TextWriter.");
Console.WriteLine("Data written to file using TextWriter.");
}
}
}
In this example, TextWriter is used to write text data to a file named textwriter_example.txt. The File.CreateText method is used to create a StreamWriter object that writes to the specified file.
C# TextReader
TextReader is an abstract class used to read a series of characters from a stream. It is the base class for reading characters from streams or files, and its common derived class is StreamReader.
TextReader Example - Reading from a File
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "textwriter_example.txt";
// Using TextReader to read data
using (TextReader reader = File.OpenText(filePath))
{
string content;
while ((content = reader.ReadLine()) != null)
{
Console.WriteLine(content);
}
}
}
}
In this example, TextReader is used to read text from textwriter_example.txt line by line. The File.OpenText method returns a StreamReader object that reads from the specified file.
C# BinaryWriter
BinaryWriter is a class in C# used to write primitive types as binary data to a stream. It is often used for writing to files or memory streams when dealing with binary data.
BinaryWriter Example - Writing Binary Data
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "binary_example.dat";
// Using BinaryWriter to write binary data
using (BinaryWriter writer = new BinaryWriter(File.Open(filePath, FileMode.Create)))
{
writer.Write(100); // Integer
writer.Write(3.14); // Double
writer.Write("Binary Data"); // String
Console.WriteLine("Data written to file using BinaryWriter.");
}
}
}
This example shows how to use BinaryWriter to write an integer, a double, and a string to a file named binary_example.dat. The data is stored in binary format, which is more efficient for storage and retrieval.
C# BinaryReader
BinaryReader is a class that allows reading primitive data types from a binary stream. It is often paired with BinaryWriter to read data stored in binary format.
BinaryReader Example - Reading Binary Data
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "binary_example.dat";
// Using BinaryReader to read binary data
using (BinaryReader reader = new BinaryReader(File.Open(filePath, FileMode.Open)))
{
int intValue = reader.ReadInt32();
double doubleValue = reader.ReadDouble();
string stringValue = reader.ReadString();
Console.WriteLine("Integer Value: " + intValue);
Console.WriteLine("Double Value: " + doubleValue);
Console.WriteLine("String Value: " + stringValue);
}
}
}
In this example, BinaryReader reads data from binary_example.dat, retrieving an integer, a double, and a string that were previously stored using BinaryWriter.
C# StringWriter
StringWriter is a class in C# that is part of the System.IO namespace. It is used to write data to a string (in-memory) instead of writing it to a file or other data source. This can be useful when you need to manipulate strings efficiently.
StringWriter Example - Writing to a String
using System;
using System.IO;
class Program
{
static void Main()
{
// Creating a StringWriter instance
using (StringWriter stringWriter = new StringWriter())
{
stringWriter.WriteLine("Hello, StringWriter!");
stringWriter.WriteLine("This is an example of writing to a string.");
// Converting StringWriter to a string
string result = stringWriter.ToString();
Console.WriteLine("Data written using StringWriter:");
Console.WriteLine(result);
}
}
}
In this example, StringWriter is used to write text data to an in-memory string. The ToString() method is then used to retrieve the written content as a string.
C# StringReader
StringReader is a class used to read data from a string (in-memory). It is often used to parse or manipulate strings, especially when working with formatted or multi-line data.
StringReader Example - Reading from a String
using System;
using System.IO;
class Program
{
static void Main()
{
string data = "First Line\nSecond Line\nThird Line";
// Using StringReader to read data
using (StringReader stringReader = new StringReader(data))
{
string line;
while ((line = stringReader.ReadLine()) != null)
{
Console.WriteLine(line);
}
}
}
}
This example demonstrates how to use StringReader to read lines from a multi-line string. It processes each line using ReadLine() in a loop.
C# FileInfo
FileInfo is a class that provides properties and methods for working with files. Unlike File, which is a static class, FileInfo offers instance methods that require the creation of an object. It allows you to manipulate file attributes and retrieve file information.
FileInfo Example - File Operations
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "fileinfo_example.txt";
// Creating FileInfo instance
FileInfo fileInfo = new FileInfo(filePath);
// Creating a file and writing data to it
using (StreamWriter writer = fileInfo.CreateText())
{
writer.WriteLine("Hello, FileInfo!");
}
// Checking if file exists
if (fileInfo.Exists)
{
Console.WriteLine("File Name: " + fileInfo.Name);
Console.WriteLine("Full Path: " + fileInfo.FullName);
Console.WriteLine("File Size: " + fileInfo.Length + " bytes");
// Deleting the file
fileInfo.Delete();
Console.WriteLine("File deleted.");
}
}
}
This example shows how to use FileInfo to create a file, write to it, check its properties, and delete it. The FileInfo instance methods provide detailed information about the file.
C# DirectoryInfo
DirectoryInfo is a class that provides methods for working with directories and subdirectories. It allows you to create, move, delete, and obtain information about directories in the file system.
DirectoryInfo Example - Directory Operations
using System;
using System.IO;
class Program
{
static void Main()
{
string directoryPath = "example_directory";
// Creating DirectoryInfo instance
DirectoryInfo dirInfo = new DirectoryInfo(directoryPath);
// Creating a directory
if (!dirInfo.Exists)
{
dirInfo.Create();
Console.WriteLine("Directory created: " + dirInfo.FullName);
}
// Displaying directory details
Console.WriteLine("Directory Name: " + dirInfo.Name);
Console.WriteLine("Parent Directory: " + dirInfo.Parent);
Console.WriteLine("Creation Time: " + dirInfo.CreationTime);
// Deleting the directory
dirInfo.Delete();
Console.WriteLine("Directory deleted.");
}
}
In this example, DirectoryInfo is used to create a new directory, display its properties, and then delete it. DirectoryInfo provides methods to handle directory-related operations easily.
C# Serialization
Serialization in C# is the process of converting an object into a format that can be easily stored or transmitted, such as binary, XML, or JSON. The most common use case is saving the state of an object to a file or sending it over a network.
Serialization Example - Object to File
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
class Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
class Program
{
static void Main()
{
Person person = new Person("John Doe", 30);
// Creating a stream to save serialized data
using (FileStream fs = new FileStream("person.dat", FileMode.Create))
{
IFormatter formatter = new BinaryFormatter();
formatter.Serialize(fs, person);
Console.WriteLine("Object serialized to 'person.dat'");
}
}
}
In this example, the Person class is marked as [Serializable]. The BinaryFormatter is used to serialize the object to a file (person.dat). The object is converted into a binary format that can be stored or transmitted.
C# Deserialization
Deserialization is the reverse process of serialization. It converts the stored format (binary, XML, JSON) back into an object that can be used in the application.
Deserialization Example - File to Object
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
class Person
{
public string Name;
public int Age;
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
class Program
{
static void Main()
{
// Creating a stream to read serialized data
using (FileStream fs = new FileStream("person.dat", FileMode.Open))
{
IFormatter formatter = new BinaryFormatter();
Person person = (Person)formatter.Deserialize(fs);
Console.WriteLine($"Deserialized Person: {person.Name}, Age: {person.Age}");
}
}
}
Here, we use BinaryFormatter.Deserialize to read the data from the person.dat file and convert it back into a Person object. The object is then accessible just like any other object in the application.
C# System.IO
System.IO is a namespace in C# that provides types for reading and writing to files, directories, and streams. It includes classes for performing file operations, such as reading and writing text, managing file paths, and manipulating file and directory attributes.
System.IO Example - Working with Files
using System;
using System.IO;
class Program
{
static void Main()
{
string filePath = "example.txt";
// Writing text to a file
File.WriteAllText(filePath, "Hello, System.IO!");
Console.WriteLine("Text written to file.");
// Reading text from a file
string content = File.ReadAllText(filePath);
Console.WriteLine("File content: " + content);
// Appending text to a file
File.AppendAllText(filePath, " This is more text.");
Console.WriteLine("Text appended to file.");
// Deleting the file
File.Delete(filePath);
Console.WriteLine("File deleted.");
}
}
In this example, we use File.WriteAllText to write a string to a file, File.ReadAllText to read the contents of the file, and File.AppendAllText to append more text. Finally, we use File.Delete to remove the file from the system.
C# Collections
Collections in C# are classes that provide data structures to store and manipulate groups of objects. They are part of the System.Collections namespace and provide more flexibility than arrays. Common collection types include List<T>, HashSet<T>, and SortedSet<T>. These collections allow developers to store, retrieve, and manipulate data more efficiently than simple arrays.
C# List<T>
The List<T> class is a generic collection in C# that represents a list of objects that can be accessed by index. Lists can grow dynamically as elements are added or removed. They are more efficient than arrays when the size of the collection is expected to change frequently.
List<T> Example
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
List fruits = new List { "Apple", "Banana", "Cherry" };
// Adding an element
fruits.Add("Mango");
// Accessing an element by index
Console.WriteLine(fruits[2]); // Output: Cherry
// Iterating through the list
foreach (var fruit in fruits)
{
Console.WriteLine(fruit);
}
// Removing an element
fruits.Remove("Banana");
// Count of elements
Console.WriteLine("List contains " + fruits.Count + " elements.");
}
}
In this example, we use the List{ collection to store a list of fruit names. We can dynamically add, remove, and access elements by index.
C# HashSet<T>
The HashSet<T> class is a collection that contains no duplicate elements. It is designed to provide fast lookup, add, and remove operations. HashSets do not preserve the order of the elements, and they are typically used when the uniqueness of elements is a priority.
HashSet<T> Example
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
HashSet numbers = new HashSet { 1, 2, 3, 4, 5 };
// Adding an element
numbers.Add(6);
// Trying to add a duplicate element (will not be added)
numbers.Add(3); // Duplicate, will not be added
// Iterating through the HashSet
foreach (var number in numbers)
{
Console.WriteLine(number); // Output: 1, 2, 3, 4, 5, 6
}
// Checking if an element exists
Console.WriteLine(numbers.Contains(3)); // Output: True
}
}
In this example, we create a HashSet and add numbers to it. The duplicate 3 is not added because HashSets only allow unique elements.
C# SortedSet<T>
The SortedSet<T> class is similar to HashSet<T>, but it maintains the elements in sorted order based on their natural ordering or a specified comparer. It ensures that there are no duplicates and automatically sorts elements as they are added.
SortedSet<T> Example
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
SortedSet cities = new SortedSet { "New York", "London", "Paris", "Berlin" };
// Adding an element
cities.Add("Tokyo");
// Trying to add a duplicate element (will not be added)
cities.Add("Paris"); // Duplicate, will not be added
// Iterating through the SortedSet (sorted order)
foreach (var city in cities)
{
Console.WriteLine(city); // Output: Berlin, London, New York, Paris, Tokyo
}
}
}
Here, we create a SortedSet{ and add cities to it. The elements are automatically sorted in alphabetical order. The duplicate Paris is not added to the collection.
C# Advanced Collections
C# provides a variety of advanced collection types such as Stack<T>, Queue<T>, LinkedList<T>, Dictionary<K,V>, SortedDictionary<K,V>, and SortedList<K,V>. These collections offer different features that are optimized for specific use cases such as LIFO (Last-In-First-Out), FIFO (First-In-First-Out), efficient access to key-value pairs, and sorted collections. Here’s an in-depth look at each of them:
C# Stack<T>
Stack<T> represents a collection of objects that follows the Last-In-First-Out (LIFO) principle. The most recent item added is the first to be removed. Stacks are commonly used in scenarios like undo functionality or recursion operations.
Stack<T> Example
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Stack books = new Stack();
// Pushing elements to the stack
books.Push("C# Programming");
books.Push("Java Fundamentals");
books.Push("Python for Data Science");
// Popping an element from the stack
Console.WriteLine(books.Pop()); // Output: Python for Data Science
// Peek to see the top element without removing
Console.WriteLine(books.Peek()); // Output: Java Fundamentals
// Iterating through the stack
foreach (var book in books)
{
Console.WriteLine(book);
}
}
}
The Push() method adds elements to the stack, Pop() removes and returns the top element, and Peek() lets you view the top element without removing it.
C# Queue<T>
Queue<T> represents a collection of objects that follows the First-In-First-Out (FIFO) principle. The first element added is the first one to be removed. Queues are used in scenarios like task scheduling or handling requests in a server.
Queue<T> Example
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Queue tasks = new Queue();
// Enqueue elements
tasks.Enqueue("Task 1");
tasks.Enqueue("Task 2");
tasks.Enqueue("Task 3");
// Dequeue an element
Console.WriteLine(tasks.Dequeue()); // Output: Task 1
// Peek to see the front element without removing
Console.WriteLine(tasks.Peek()); // Output: Task 2
// Iterating through the queue
foreach (var task in tasks)
{
Console.WriteLine(task);
}
}
}
The Enqueue() method adds elements to the queue, Dequeue() removes and returns the front element, and Peek() allows you to view the front element without removing it.
C# LinkedList<T>
LinkedList<T> is a collection that allows you to store elements in a doubly linked list, meaning each element (node) contains a reference to the next and previous nodes. This collection is useful when you need fast insertions and deletions from both ends of the list.
LinkedList<T> Example
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
LinkedList names = new LinkedList();
// Adding elements
names.AddLast("John");
names.AddLast("Jane");
names.AddFirst("Tom");
// Remove elements
names.Remove("Jane");
// Iterate through the linked list
foreach (var name in names)
{
Console.WriteLine(name); // Output: Tom, John
}
}
}
In this example, we use the AddLast() and AddFirst() methods to insert elements at the end and beginning of the list, respectively. Remove() is used to delete an element.
C# Dictionary<K,V>
Dictionary<K,V> is a collection of key-value pairs. It allows you to store data where each element is identified by a unique key. This is useful for fast lookups where each key maps to a specific value, like storing user information by username.
Dictionary<K,V> Example
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
Dictionary capitals = new Dictionary();
// Adding key-value pairs
capitals.Add("USA", "Washington, D.C.");
capitals.Add("France", "Paris");
capitals.Add("Germany", "Berlin");
// Accessing values by key
Console.WriteLine(capitals["USA"]); // Output: Washington, D.C.
// Iterating through the dictionary
foreach (var entry in capitals)
{
Console.WriteLine($"{entry.Key}: {entry.Value}");
}
}
}
We use the Add() method to insert key-value pairs into the dictionary. Values are accessed using their corresponding keys.
C# SortedDictionary<K,V>
SortedDictionary<K,V> is similar to Dictionary<K,V>, but the key-value pairs are stored in sorted order based on the key. This is helpful when you need a dictionary that always maintains keys in a sorted sequence.
SortedDictionary<K,V> Example
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
SortedDictionary students = new SortedDictionary();
// Adding key-value pairs
students.Add(2, "Alice");
students.Add(1, "Bob");
students.Add(3, "Charlie");
// Iterating through the sorted dictionary
foreach (var student in students)
{
Console.WriteLine($"{student.Key}: {student.Value}");
}
}
}
The elements in the SortedDictionary are automatically sorted based on the keys. In this case, the keys are integers representing student IDs.
C# SortedList<K,V>
SortedList<K,V> is similar to a SortedDictionary, but it uses an array-like structure internally. It maintains key-value pairs in sorted order based on the keys and provides faster access times for key-value pairs than a SortedDictionary.
SortedList<K,V> Example
using System;
using System.Collections.Generic;
class Program
{
static void Main()
{
SortedList countries = new SortedList();
// Adding key-value pairs
countries.Add(3, "Canada");
countries.Add(1, "USA");
countries.Add(2, "Mexico");
// Iterating through the sorted list
foreach (var country in countries)
{
Console.WriteLine($"{country.Key}: {country.Value}");
}
}
}
In this example, SortedList keeps the key-value pairs in sorted order based on the keys. It is particularly useful when both sorting and efficient access are needed.
C# Generics
Generics in C# allow you to define classes, methods, and data structures with type parameters. This enables type safety without compromising performance, allowing you to work with different types while ensuring type correctness at compile time.
Generic Method Example
using System;
class Program
{
// Generic method that works with any type
static void Print<T>(T value)
{
Console.WriteLine(value);
}
static void Main()
{
// Calling generic method with different data types
Print<int>(42); // Output: 42
Print<string>("Hello, Generics!"); // Output: Hello, Generics!
}
}
In this example, the Print<T> method is generic and can accept any type. We call it with an integer and a string, showcasing how generics can provide flexibility while maintaining type safety.
Generic Class Example
using System;
class Box<T>
{
private T value;
public void SetValue(T value)
{
this.value = value;
}
public T GetValue()
{
return value;
}
}
class Program
{
static void Main()
{
// Creating an instance of the generic class with int type
Box<int> intBox = new Box<int>();
intBox.SetValue(10);
Console.WriteLine(intBox.GetValue()); // Output: 10
// Creating an instance of the generic class with string type
Box<string> strBox = new Box<string>();
strBox.SetValue("Generics are cool!");
Console.WriteLine(strBox.GetValue()); // Output: Generics are cool!
}
}
Here, the Box<T> class can store any type of object. By creating instances of Box<T> with different types (like int and string), we can reuse the same class for various data types.
C# Delegates
A delegate is a type that represents references to methods with a particular parameter list and return type. Delegates are used to pass methods as arguments to other methods, enabling flexibility and event-driven programming in C#.
Delegate Declaration and Usage
using System;
delegate void PrintMessage(string message);
class Program
{
static void DisplayMessage(string message)
{
Console.WriteLine(message);
}
static void Main()
{
// Instantiate the delegate and point it to the DisplayMessage method
PrintMessage printDelegate = new PrintMessage(DisplayMessage);
// Invoking the delegate
printDelegate("Hello, Delegates!"); // Output: Hello, Delegates!
}
}
In this example, we declare a PrintMessage delegate that points to the DisplayMessage method. By invoking the delegate, we call the method it references, demonstrating how delegates facilitate method passing.
Multicast Delegate Example
using System;
delegate void PrintMessage(string message);
class Program
{
static void DisplayMessage(string message)
{
Console.WriteLine(message);
}
static void DisplayMessageUpperCase(string message)
{
Console.WriteLine(message.ToUpper());
}
static void Main()
{
// Create a delegate instance and add multiple methods to the invocation list
PrintMessage printDelegate = new PrintMessage(DisplayMessage);
printDelegate += new PrintMessage(DisplayMessageUpperCase);
// Invoking the multicast delegate
printDelegate("Multicast Delegate Example!");
}
}
In this multicast delegate example, we add multiple methods (DisplayMessage and DisplayMessageUpperCase) to the printDelegate delegate. When invoked, both methods are executed sequentially.
Anonymous Delegate Example
using System;
class Program
{
static void Main()
{
// Using an anonymous delegate to avoid declaring a separate method
PrintMessage printDelegate = delegate (string message)
{
Console.WriteLine(message);
};
// Invoking the anonymous delegate
printDelegate("Anonymous Delegate Example!"); // Output: Anonymous Delegate Example!
}
}
delegate void PrintMessage(string message);
Here, we use an anonymous delegate, which allows us to define the method inline without creating a separate method. This is particularly useful for small, one-off methods that do not require a dedicated function.
C# Multithreading
Multithreading in C# allows multiple threads to execute concurrently within a single application. This enables better resource utilization and performance, especially in tasks that can be executed independently.
Basic Multithreading Example
using System;
using System.Threading;
class Program
{
// Method to be run on a separate thread
static void PrintNumbers()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine(i);
Thread.Sleep(1000); // Pause for 1 second
}
}
static void Main()
{
// Creating a new thread
Thread thread = new Thread(PrintNumbers);
thread.Start(); // Start the thread
// Main thread continues executing
Console.WriteLine("Main thread is still running...");
thread.Join(); // Wait for the new thread to finish
Console.WriteLine("New thread has finished.");
}
}
In this example, a new thread is created using the Thread class, and the PrintNumbers method is executed on that thread. The Main thread continues executing independently, and thread.Join() ensures that the main thread waits for the new thread to finish before proceeding.
C# Thread Life Cycle
A thread in C# undergoes several stages in its life cycle:
- New: The thread is created but not yet started.
- Runnable: The thread is ready to run but waiting for CPU time.
- Running: The thread is currently executing.
- Blocked: The thread is waiting for resources or another thread to complete.
- Terminated: The thread has completed its execution or was aborted.
Thread Life Cycle Example
using System;
using System.Threading;
class Program
{
static void PrintThreadLifeCycle()
{
Console.WriteLine("Thread is now in Running state.");
Thread.Sleep(1000); // Simulate some work
Console.WriteLine("Thread is now terminating.");
}
static void Main()
{
Thread thread = new Thread(PrintThreadLifeCycle);
Console.WriteLine("Thread state: " + thread.ThreadState); // Thread state: Unstarted
thread.Start(); // Thread state: Running
thread.Join(); // Wait for thread to finish
Console.WriteLine("Thread state after completion: " + thread.ThreadState); // Thread state: Stopped
}
}
In this example, we track the state of the thread at different points. The ThreadState property helps to monitor the thread's life cycle from the "Unstarted" state to the "Running" state, and finally to "Stopped" after completion.
C# Thread Class
The Thread class in C# provides functionality to create and manage threads. It allows you to start a new thread, check its state, and control its execution. Key methods include Start(), Join(), Sleep(), and Abort().
Thread Class Example
using System;
using System.Threading;
class Program
{
static void DisplayThreadId()
{
Console.WriteLine($"Thread ID: {Thread.CurrentThread.ManagedThreadId}");
}
static void Main()
{
// Create and start a new thread
Thread thread = new Thread(DisplayThreadId);
thread.Start();
// Main thread display its own ID
Console.WriteLine($"Main Thread ID: {Thread.CurrentThread.ManagedThreadId}");
}
}
In this example, the ManagedThreadId property is used to display the unique ID of the current thread. The Main thread and the new thread both have different IDs, indicating they are separate threads of execution.
C# Main Thread
The Main thread is the entry point of any C# application. It runs automatically when the application starts and executes the Main() method. In a multithreading application, the main thread can create and manage other threads.
Main Thread Example
using System;
using System.Threading;
class Program
{
static void Main()
{
Console.WriteLine("Main thread started.");
// Create a new thread and start it
Thread thread = new Thread(() => Console.WriteLine("New thread running"));
thread.Start();
// Main thread continues executing
Console.WriteLine("Main thread is still running...");
thread.Join(); // Wait for the new thread to finish
Console.WriteLine("Main thread has finished.");
}
}
In this example, the Main thread starts a new thread that executes a separate task, and the main thread continues its execution. The Join() method is used to make the main thread wait for the new thread to complete before continuing.
C# Thread Example
In C#, you can create a thread using the Thread class and execute a specific method in that thread. Threads help execute multiple tasks simultaneously.
Basic Thread Example
using System;
using System.Threading;
class Program
{
static void PrintNumbers()
{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine(i);
Thread.Sleep(1000); // Sleep for 1 second
}
}
static void Main()
{
Thread thread = new Thread(PrintNumbers); // Creating new thread
thread.Start(); // Start the thread
// Main thread continues its execution
Console.WriteLine("Main thread continues executing...");
thread.Join(); // Wait for the thread to finish
Console.WriteLine("New thread has finished.");
}
}
This example demonstrates creating a new thread that prints numbers and puts the thread to sleep for one second between each print. The main thread continues executing and waits for the new thread to finish using Join().
C# Thread Sleep
The Thread.Sleep() method is used to pause the execution of a thread for a specified amount of time (in milliseconds). It's commonly used to simulate delays or pauses in multithreaded applications.
Thread Sleep Example
using System;
using System.Threading;
class Program
{
static void DisplayMessage()
{
Console.WriteLine("Thread started...");
Thread.Sleep(2000); // Sleep for 2 seconds
Console.WriteLine("Thread finished after 2 seconds.");
}
static void Main()
{
Thread thread = new Thread(DisplayMessage);
thread.Start();
thread.Join();
}
}
In this example, the Thread.Sleep(2000) pauses the thread for 2 seconds before printing the second message. The Join() method ensures the main thread waits for the thread to finish before continuing.
C# Thread Abort
The Thread.Abort() method is used to abort a thread. However, it is not recommended to use it, as it can terminate the thread in an unsafe manner. It is typically replaced by more graceful methods like CancellationToken in modern applications.
Thread Abort Example
using System;
using System.Threading;
class Program
{
static void RunThread()
{
while (true)
{
Console.WriteLine("Thread running...");
Thread.Sleep(1000);
}
}
static void Main()
{
Thread thread = new Thread(RunThread);
thread.Start();
Thread.Sleep(5000); // Let the thread run for 5 seconds
thread.Abort(); // Abort the thread after 5 seconds
Console.WriteLine("Thread aborted.");
}
}
In this example, we create a thread that continuously prints a message every second. After 5 seconds, we call Abort() to terminate the thread. Keep in mind that using Abort() can leave resources in an inconsistent state, so it should be avoided in favor of safer alternatives.
C# Thread Join
The Thread.Join() method allows the main thread (or another thread) to wait for the completion of a specific thread before continuing its execution.
Thread Join Example
using System;
using System.Threading;
class Program
{
static void PrintMessages()
{
Console.WriteLine("Message from the thread.");
}
static void Main()
{
Thread thread = new Thread(PrintMessages);
thread.Start();
thread.Join(); // Wait for the thread to finish before proceeding
Console.WriteLine("Main thread continues after thread finishes.");
}
}
Here, Join() is used to make the main thread wait until the created thread has completed its task (printing the message) before the main thread continues.
C# Thread Name
The Thread.Name property allows you to set a name for the thread, making it easier to identify and debug in multi-threaded applications.
Thread Name Example
using System;
using System.Threading;
class Program
{
static void DisplayThreadInfo()
{
Console.WriteLine($"Current Thread: {Thread.CurrentThread.Name}");
}
static void Main()
{
Thread thread = new Thread(DisplayThreadInfo);
thread.Name = "WorkerThread"; // Setting the thread name
thread.Start();
thread.Join();
}
}
In this example, we set the name of the thread to "WorkerThread" and print that name within the thread. This helps identify the thread in complex applications.
C# ThreadPriority
The Thread.Priority property is used to set the priority of a thread, which can influence the order in which threads are scheduled to run by the operating system.
ThreadPriority Example
using System;
using System.Threading;
class Program
{
static void PrintMessage()
{
Console.WriteLine("Thread with higher priority running...");
}
static void Main()
{
Thread highPriorityThread = new Thread(PrintMessage);
highPriorityThread.Priority = ThreadPriority.AboveNormal; // Set high priority
highPriorityThread.Start();
Thread lowPriorityThread = new Thread(PrintMessage);
lowPriorityThread.Priority = ThreadPriority.BelowNormal; // Set low priority
lowPriorityThread.Start();
highPriorityThread.Join();
lowPriorityThread.Join();
}
}
In this example, we create two threads and set their priorities using the ThreadPriority enum. The higher-priority thread is likely to be executed before the lower-priority one, but the actual scheduling is still determined by the operating system.
C# Synchronization
In C#, synchronization is a technique to control the access of multiple threads to shared resources. This helps prevent race conditions, where multiple threads modify shared data simultaneously.
The most common synchronization mechanisms include locking using the lock keyword and using other synchronization constructs such as Monitor, Mutex, and Semaphore.
Using the lock Keyword
using System;
using System.Threading;
class Program
{
static int counter = 0; // Shared resource
static readonly object lockObject = new object();
static void IncrementCounter()
{
lock (lockObject)
{
// Critical section
counter++;
Console.WriteLine("Counter: " + counter);
}
}
static void Main()
{
Thread thread1 = new Thread(IncrementCounter);
Thread thread2 = new Thread(IncrementCounter);
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
}
}
In this example, two threads are incrementing a shared counter variable. The lock statement is used to ensure that only one thread can access the critical section at a time, preventing race conditions.
Monitor Class Example
using System;
using System.Threading;
class Program
{
static int counter = 0;
static readonly object lockObject = new object();
static void IncrementCounter()
{
Monitor.Enter(lockObject);
try
{
counter++;
Console.WriteLine("Counter: " + counter);
}
finally
{
Monitor.Exit(lockObject); // Ensure that the lock is released
}
}
static void Main()
{
Thread thread1 = new Thread(IncrementCounter);
Thread thread2 = new Thread(IncrementCounter);
thread1.Start();
thread2.Start();
thread1.Join();
thread2.Join();
}
}
In this example, instead of using lock, we are using Monitor.Enter and Monitor.Exit to manually acquire and release a lock. This gives more control over the locking process and is useful in more complex scenarios.
C# Web Service
A Web Service in C# is an application that provides a method to allow different applications to communicate with each other over the internet. A common technology used to build web services in C# is ASP.NET Web API or WCF (Windows Communication Foundation). In this section, we’ll focus on a simple example of creating a SOAP-based Web Service using ASP.NET.
Basic Web Service Example
using System;
using System.Web.Services;
public class MyWebService : WebService
{
[WebMethod]
public string HelloWorld()
{
return "Hello, world!";
}
}
public class Program
{
static void Main()
{
Console.WriteLine("Web Service is running...");
// Normally here you'd run the service in an ASP.NET environment.
}
}
In this simple example, we created a basic web service using the WebService class in C#. The HelloWorld() method is marked with the WebMethod attribute, making it available as part of the web service. When this service is hosted, external applications can call this method via HTTP requests and receive a response.
ASP.NET Web Service Hosting
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.5" />
</system.web>
<system.webServer>
<handlers>
<add name="WebServiceHandler" path="*.asmx" verb="*" type="System.Web.Script.Services.ScriptHandlerFactory" resourceType="Unspecified" />
</handlers>
</system.webServer>
</configuration>
This is a sample configuration for hosting an ASP.NET web service in IIS (Internet Information Services). The .asmx file extension is used to define the endpoint for the web service. The configuration file ensures that the correct handler is used to process incoming requests to the service.
Consuming the Web Service in a Client
using System;
class Program
{
static void Main()
{
MyWebService myService = new MyWebService();
string result = myService.HelloWorld();
Console.WriteLine(result); // Outputs: Hello, world!
}
}
To consume the web service from a client application, you simply create an instance of the web service class and call the desired method. In this case, the client calls the HelloWorld() method, which returns the string "Hello, world!".
Congratulations on Completing Your C# Journey!
Congratulations on reaching the end of this C# learning path! You have now acquired a solid understanding of key concepts such as synchronization, web services, object-oriented programming, and much more. By mastering these foundational elements, you're well-equipped to dive deeper into more advanced C# topics and start building robust, high-performance applications.
Your Future Roadmap with C#
As you continue to grow your C# skills, there are several areas where you can deepen your knowledge:
- Advanced C# Features: Explore topics like async programming, parallel computing, LINQ, reflection, and more advanced design patterns.
- Web Development with ASP.NET Core: Learn how to build scalable, secure, and high-performance web applications using the latest version of ASP.NET Core.
- Cloud Development: Expand your expertise into cloud computing platforms like Microsoft Azure, where you can create serverless applications and deploy C# apps at scale.
- Game Development with Unity: C# is the primary programming language for Unity, one of the most popular game development engines. If you're interested in game development, this could be an exciting next step!
- Desktop and Mobile Apps: Explore building desktop applications with Windows Forms or WPF (Windows Presentation Foundation), or even cross-platform mobile apps with Xamarin.
C# Career Options
With C# being one of the most widely-used languages in the software development industry, the career opportunities are vast. Here are some career paths that are well-suited for C# developers:
- C# Developer: Work on building and maintaining applications using C# in various domains such as enterprise solutions, APIs, or cloud-based services.
- ASP.NET Developer: Specialize in building web applications, RESTful APIs, and microservices using ASP.NET Core, MVC, and Web API frameworks.
- Game Developer: Use C# to develop interactive and visually-rich games with Unity, working in the gaming industry or as an indie developer.
- Software Engineer: Design and implement software solutions for businesses, often integrating C# with other technologies like SQL Server and cloud services.
- Mobile App Developer: With Xamarin, C# developers can build cross-platform mobile applications for iOS and Android.
- Full-Stack Developer: Learn both front-end and back-end development, leveraging C# in combination with front-end technologies like JavaScript or Blazor.
What's Next?
Now that you have a strong foundation in C#, it's time to think about where you want to go next. Whether it's mastering advanced C# concepts, exploring new frameworks, or applying your knowledge to real-world projects, the future is full of opportunities. Take the time to build projects, contribute to open-source, or even try to build a portfolio to showcase your skills. Continuous learning is key in the ever-evolving world of technology.
Remember, your journey with C# has just begun. Embrace the challenges ahead, and continue to build on your knowledge. The skills you've developed will help you in countless areas of software development.