0% found this document useful (0 votes)
20 views

C Sharp_Advance

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
20 views

C Sharp_Advance

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 68

Real-Time Examples of Events, Delegates, and Lambda Expressions in C#

Events, delegates, and lambda expressions are powerful features in C# that enable dynamic method invocation, event-
driven programming, and concise code. Below are explanations and real-time examples of these concepts.

1. Course Structure of Events, Delegates, and Lambda Expressions


Events: Events are used to provide notifications in an application. When something of interest happens (like a
button click, a data change, etc.), events allow components to react to it. They are typically based on delegates.
Delegates: A delegate is a type-safe method reference. It defines a method signature and can refer to any
method with that signature. Delegates are used to pass methods as arguments and allow dynamic method
invocation.
Lambda Expressions: Lambda expressions provide a concise way to define anonymous methods using a simple
syntax. They are particularly useful when used with delegates and LINQ queries.

2. Roles of Events, Delegates, and Event Handlers in C#


Delegate: A delegate acts as a pointer to a method. It defines the signature of the method that can be called.
Delegates can be used to call methods dynamically.
Event: An event is a special type of delegate that allows methods to subscribe to it (event handlers). It is
commonly used in event-driven programming where an event triggers actions in response to user interactions or
system changes.
Event Handler: An event handler is a method that handles the event when it is triggered. It is a method that
conforms to the event's delegate signature.

3. Delegates in C#
Delegates allow methods to be passed as parameters, invoked dynamically, and decouple event sources from their
handlers.
Example: Basic Delegate Usage
using System; csharp

// Delegate Declaration
public delegate void PrintMessageDelegate(string message);

public class Program


{
public static void PrintMessage(string message)
{
Console.WriteLine(message);
}
public static void Main(string[] args)
{
// Creating Delegate instance
PrintMessageDelegate del = new PrintMessageDelegate(PrintMessage);

// Invoking the delegate


del("Hello, C# Delegates!");
}
}

Explanation: A PrintMessageDelegate is declared to point to methods that take a string argument and
return void . The delegate is instantiated and invoked with a string message.

4. Multicast Delegates in C#
Multicast Delegates are delegates that can hold references to multiple methods. When invoked, they call all the
methods in the delegate's invocation list.
Example: Using Multicast Delegates
using System; csharp

// Delegate Declaration
public delegate void NotifyDelegate(string message);

public class Program


{
public static void NotifyByEmail(string message)
{
Console.WriteLine("Email: " + message);
}

public static void NotifyBySMS(string message)


{
Console.WriteLine("SMS: " + message);
}

public static void Main(string[] args)


{
// Creating Multicast Delegate
NotifyDelegate notifyDelegate = new NotifyDelegate(NotifyByEmail);
notifyDelegate += NotifyBySMS; // Add another method to the delegate

// Invoking the multicast delegate


notifyDelegate("Your order has been shipped!");
}
}
Explanation: The NotifyDelegate multicast delegate can call both NotifyByEmail and NotifyBySMS
methods. Both methods are executed when the delegate is invoked.

5. Delegates Real-Time Example in C#


Real-Time Example of Delegates: A common real-world use of delegates is in event handling, where delegates are
used to define the signature of the event handler methods.
Example: Delegate in Event Handling
using System; csharp

// Delegate Definition
public delegate void ButtonClickDelegate();

public class Button


{
// Declare an event based on the delegate
public event ButtonClickDelegate Click;

// Trigger the event


public void OnClick()
{
Console.WriteLine("Button clicked!");
Click?.Invoke(); // Invokes the event handlers if any are attached
}
}

public class Program


{
public static void HandleClick()
{
Console.WriteLine("Button click event handled.");
}

public static void Main(string[] args)


{
Button button = new Button();
button.Click += HandleClick; // Subscribe to the event

button.OnClick(); // Simulate button click


}
}

Explanation: The Button class has an event Click that uses a delegate type ButtonClickDelegate .
The OnClick method triggers the event, and the HandleClick method is invoked when the event occurs.
6. Generic Delegates in C#
Generic Delegates: These allow the creation of delegates that work with different types without the need for creating
multiple delegates for each specific type.
Example: Generic Delegate
using System; csharp

// Generic Delegate Declaration


public delegate TResult FuncDelegate<in T, out TResult>(T arg);

public class Program


{
public static int Square(int number)
{
return number * number;
}

public static void Main(string[] args)


{
// Creating a Generic Delegate
FuncDelegate<int, int> squareDelegate = Square;

// Invoking the delegate


int result = squareDelegate(5);
Console.WriteLine("Square of 5 is: " + result);
}
}

Explanation: FuncDelegate<T, TResult> is a generic delegate that can accept any type T and return a
result of type TResult . Here, it's used to calculate the square of an integer.

7. Anonymous Method in C#
Anonymous Methods: Anonymous methods allow you to define a method inline without having to declare a named
method.
Example: Anonymous Method
using System; csharp

public delegate void PrintMessageDelegate(string message);

public class Program


{
public static void Main(string[] args)
{
// Anonymous method
PrintMessageDelegate del = delegate(string message)
{
Console.WriteLine(message);
};

// Invoking the delegate


del("Hello from Anonymous Method!");
}
}

Explanation: The anonymous method is defined inline using the delegate keyword. It is then assigned to the
delegate and invoked just like a normal method.

8. Lambda Expressions in C#
Lambda Expressions: Lambda expressions are a more concise way of writing anonymous methods. They are often
used with delegates, LINQ, and other functional programming scenarios.
Example: Lambda Expression
using System; csharp

public class Program


{
public static void Main(string[] args)
{
// Lambda expression to square a number
Func<int, int> square = num => num * num;

// Invoking the lambda expression


Console.WriteLine("Square of 5 is: " + square(5));
}
}

Explanation: The lambda expression num => num * num is a concise representation of a method that
squares a number. It is assigned to a Func<int, int> delegate and invoked with the number 5 .

9. Events in C# with Examples


Events are based on delegates and allow subscribers to listen for specific actions or state changes in an application.
Example: Event Handling in C#
using System; csharp

// Delegate for the event handler


public delegate void ProcessCompletedEventHandler(string message);
public class Process
{
// Event declaration
public event ProcessCompletedEventHandler ProcessCompleted;

// Method to trigger the event


public void StartProcess()
{
Console.WriteLine("Process Started");
// Simulating some process
System.Threading.Thread.Sleep(1000);
// Trigger event after the process is complete
OnProcessCompleted("Process has completed successfully.");
}

// Method to trigger the event


protected virtual void OnProcessCompleted(string message)
{
ProcessCompleted?.Invoke(message); // Safe invocation of event
}
}

public class Program


{
public static void Main(string[] args)
{
Process process = new Process();
process.ProcessCompleted += Process_Completed; // Subscribe to the event

process.StartProcess(); // Start the process


}

// Event handler method


private static void Process_Completed(string message)
{
Console.WriteLine(message);
}
}

Explanation:
Process class defines an event ProcessCompleted based on the
ProcessCompletedEventHandler delegate.
The StartProcess method simulates a process and triggers the ProcessCompleted event when
done.
The Process_Completed method handles the event and prints the message.

Summary of Concepts:
Events: Used for communication between objects (often used in UI and event-driven applications).
Delegates: Type-safe function pointers, used to reference methods and allow dynamic invocation.
Multicast Delegates: Delegates that point to multiple methods and invoke them when called.
Generic Delegates: Delegates that work with
any data type, making the code more flexible and reusable.
Anonymous Methods: Inline methods without names, often used for simple, short logic.
Lambda Expressions: A more concise and readable way to write anonymous methods, often used with LINQ or
delegates.
Event Handling: Delegates are used to create events, and event handlers respond to these events when they are
triggered.
These concepts allow for powerful, flexible, and efficient programming techniques in C#, especially in GUI
applications, event-driven programming, and dynamic method invocation.
Real-Time Examples of Multithreading in C#
Multithreading in C# allows concurrent execution of code, improving the performance of applications by running tasks
simultaneously. It is especially useful in CPU-bound tasks, I/O-bound operations, and applications with high
responsiveness needs (e.g., GUI applications).
Here are detailed explanations and real-world examples for various multithreading concepts in C#.

1. Multithreading in C#
Multithreading allows multiple threads to run concurrently, enabling efficient utilization of system resources (such as
CPU). It helps to speed up CPU-bound tasks by performing several operations simultaneously.
Example: Simple Multithreading
using System; csharp
using System.Threading;

public class Program


{
public static void Main()
{
Thread thread = new Thread(DoWork);
thread.Start();
Console.WriteLine("Main thread running...");
}

static void DoWork()


{
Console.WriteLine("Worker thread executing...");
}
}
Explanation: A new thread thread is created and started. The DoWork method runs on this new thread
while the main thread continues execution.

2. Thread Class in C#
The Thread class represents a thread in C#. It allows creating, starting, and managing threads.
Example: Using Thread Class
using System; csharp
using System.Threading;

public class Program


{
static void Main(string[] args)
{
Thread thread1 = new Thread(PrintNumbers);
thread1.Start();
}

static void PrintNumbers()


{
for (int i = 1; i <= 5; i++)
{
Console.WriteLine(i);
Thread.Sleep(1000); // Simulate work
}
}
}

Explanation: The Thread class is used to create a new thread that executes the PrintNumbers method.

3. How to Pass Data to Thread Function in Type Safe Manner in C#


You can pass data to threads using parameters in thread methods or by using thread-safe mechanisms like
ParameterizedThreadStart .

Example: Passing Data to Thread Function


using System; csharp
using System.Threading;

public class Program


{
static void Main(string[] args)
{
Thread thread = new Thread(PrintMessage);
thread.Start("Hello from Thread!");
}

static void PrintMessage(object message)


{
Console.WriteLine(message);
}
}

Explanation: The message "Hello from Thread!" is passed to the PrintMessage method through the
Start method.

4. How to Retrieve Data from a Thread Function in C#


To retrieve data from a thread, you can use thread synchronization techniques like Thread.Join() , Task , or
ConcurrentQueue .

Example: Retrieving Data from a Thread


using System; csharp
using System.Threading;

public class Program


{
static void Main()
{
Thread thread = new Thread(DoWork);
thread.Start();
thread.Join(); // Wait for the thread to finish
Console.WriteLine("Main thread completed.");
}

static void DoWork()


{
Console.WriteLine("Worker thread executing...");
Thread.Sleep(2000); // Simulate work
}
}

Explanation: The Join() method ensures that the main thread waits until the worker thread completes before
it continues execution.

5. Join Method and IsAlive Property of Thread Class in C#


Join(): Makes the calling thread wait for the thread to finish before continuing.
IsAlive: Indicates whether a thread is still running.
Example: Using Join and IsAlive
using System; csharp
using System.Threading;

public class Program


{
static void Main()
{
Thread thread = new Thread(DoWork);
thread.Start();

while (thread.IsAlive)
{
Console.WriteLine("Waiting for thread to finish...");
Thread.Sleep(500); // Periodic check
}

Console.WriteLine("Main thread completed.");


}

static void DoWork()


{
Console.WriteLine("Worker thread running...");
Thread.Sleep(2000);
}
}

Explanation: The IsAlive property is checked in a loop. The main thread waits until the worker thread
completes.

6. Thread Synchronization in C#
Thread Synchronization is necessary when multiple threads access shared resources to avoid conflicts. It ensures
that only one thread can access a shared resource at a time.

7. Lock in C#
The lock keyword ensures that a block of code is executed by only one thread at a time.
Example: Using Lock for Thread Synchronization
using System; csharp
using System.Threading;

public class Program


{
static object lockObj = new object();
static void Main(string[] args)
{
Thread thread1 = new Thread(IncrementCounter);
Thread thread2 = new Thread(IncrementCounter);

thread1.Start();
thread2.Start();
}

static void IncrementCounter()


{
lock (lockObj)
{
Console.WriteLine("Counter incremented by thread: " + Thread.CurrentThread.
ManagedThreadId);
Thread.Sleep(1000); // Simulate work
}
}
}

Explanation: The lock ensures that only one thread at a time can execute the critical section, preventing race
conditions.

8. Monitor Class in C#
The Monitor class provides a mechanism for thread synchronization similar to lock , but with more control.
Example: Using Monitor for Synchronization
using System; csharp
using System.Threading;

public class Program


{
static object lockObj = new object();

static void Main()


{
Thread thread1 = new Thread(DoWork);
Thread thread2 = new Thread(DoWork);

thread1.Start();
thread2.Start();
}

static void DoWork()


{
Monitor.Enter(lockObj);
try
{
Console.WriteLine("Thread working...");
Thread.Sleep(1000); // Simulate work
}
finally
{
Monitor.Exit(lockObj);
}
}
}

Explanation: Monitor.Enter and Monitor.Exit are used to acquire and release the lock, ensuring only
one thread accesses the critical section.

9. Mutex Class in C#
A Mutex is used for synchronization between threads or even processes. It is more robust than lock and can work
across multiple processes.
Example: Using Mutex
using System; csharp
using System.Threading;

public class Program


{
static Mutex mutex = new Mutex();

static void Main()


{
Thread thread1 = new Thread(DoWork);
Thread thread2 = new Thread(DoWork);

thread1.Start();
thread2.Start();
}

static void DoWork()


{
mutex.WaitOne();
try
{
Console.WriteLine("Thread working...");
Thread.Sleep(1000); // Simulate work
}
finally
{
mutex.ReleaseMutex();
}
}
}

Explanation: Mutex.WaitOne ensures that only one thread can access the critical section at a time, even
across different processes.

10. Semaphore Class in C#


A Semaphore is used to limit the number of threads that can access a resource concurrently.
Example: Using Semaphore
using System; csharp
using System.Threading;

public class Program


{
static Semaphore semaphore = new Semaphore(2, 2); // Maximum 2 threads

static void Main()


{
Thread thread1 = new Thread(DoWork);
Thread thread2 = new Thread(DoWork);
Thread thread3 = new Thread(DoWork);

thread1.Start();
thread2.Start();
thread3.Start();
}

static void DoWork()


{
semaphore.WaitOne(); // Enter critical section
try
{
Console.WriteLine("Thread working...");
Thread.Sleep(1000); // Simulate work
}
finally
{
semaphore.Release(); // Exit critical section
}
}
}

Explanation: The semaphore allows only two threads to enter the critical section at a time.
11. SemaphoreSlim Class in C#
SemaphoreSlim is a lightweight version of Semaphore , designed for use within a single process.

12. Deadlock in C#
Deadlock occurs when two or more threads are blocked forever, waiting for each other to release resources. This
happens when circular dependencies are formed between locks.
Example: Deadlock Scenario
using System; csharp
using System.Threading;

public class Program


{
static object lock1 = new object();
static object lock2 = new object();

static void Main()


{
Thread thread1 = new Thread(DeadlockScenario1);
Thread thread2 = new Thread(DeadlockScenario2);

thread1.Start();
thread2.Start();
}

static void DeadlockScenario1()


{
lock

(lock1) { Thread.Sleep(100); lock (lock2); // Wait for lock2 } }


static void DeadlockScenario2()
{
lock (lock2)
{
Thread.Sleep(100);
lock (lock1); // Wait for lock1
}
}

}
- **Explanation**: This scenario causes a deadlock, as thread1 holds `lock1` and waits
for `lock2`, while thread2 holds `lock2` and waits for `lock1`.
---

### **13. Performance Testing of a Multithreaded Application**

Performance testing involves measuring the execution time and resource consumption of a
multithreaded application. Tools like **Visual Studio Profiler**, **BenchmarkDotNet**,
and **Thread Profiling** can be used to test and optimize multithreaded code.

---

### **14. Thread Pool in C#**

The **Thread Pool** provides a pool of worker threads that can be used to execute multi
ple tasks concurrently without the need to create new threads.

#### **Example: Using ThreadPool**


```csharp
using System;
using System.Threading;

public class Program


{
static void Main()
{
ThreadPool.QueueUserWorkItem(DoWork);
Console.WriteLine("Main thread completed.");
}

static void DoWork(object state)


{
Console.WriteLine("Thread pool thread executing...");
}
}

Explanation: ThreadPool.QueueUserWorkItem places the DoWork method in the thread pool for
execution.

15. Foreground and Background Threads in C#


Foreground Threads: These threads keep the application running until they finish.
Background Threads: These threads are terminated when the main application ends, even if they are still
running.
Example: Foreground and Background Threads
using System; csharp
using System.Threading;

public class Program


{
static void Main()
{
Thread foregroundThread = new Thread(DoWork);
Thread backgroundThread = new Thread(DoWork);

backgroundThread.IsBackground = true; // Set background thread


foregroundThread.Start();
backgroundThread.Start();

Console.WriteLine("Main thread completed.");


}

static void DoWork()


{
Thread.Sleep(1000);
Console.WriteLine("Worker thread finished.");
}
}

Explanation: The background thread will be terminated when the main thread completes, while the foreground
thread will continue until it finishes.

16. AutoResetEvent and ManualResetEvent in C#


These classes are used for inter-thread communication. They allow threads to wait for a signal from another thread
before continuing execution.
Example: Using AutoResetEvent
using System; csharp
using System.Threading;

public class Program


{
static AutoResetEvent autoEvent = new AutoResetEvent(false);

static void Main()


{
Thread thread = new Thread(WaitForSignal);
thread.Start();
Thread.Sleep(1000); // Simulate work
autoEvent.Set(); // Signal the waiting thread
}

static void WaitForSignal()


{
Console.WriteLine("Thread waiting for signal...");
autoEvent.WaitOne(); // Wait for signal
Console.WriteLine("Thread received signal.");
}
}

Explanation: The AutoResetEvent is used to signal the worker thread that it can continue execution.

17. Thread Life Cycle in C#


A thread goes through various states in its lifecycle: Unstarted, Runnable, Running, Blocked, Waiting, and
Terminated.

18. Threads Priorities in C#


Thread priorities control the order in which threads are executed. Threads can be assigned different priority levels
(e.g., Low, Normal, High).
Example: Setting Thread Priority
using System; csharp
using System.Threading;

public class Program


{
static void Main()
{
Thread thread1 = new Thread(DoWork);
Thread thread2 = new Thread(DoWork);

thread1.Priority = ThreadPriority.Highest;
thread2.Priority = ThreadPriority.Lowest;

thread1.Start();
thread2.Start();
}

static void DoWork()


{
Console.WriteLine("Thread executing...");
}
}

Explanation: Thread1 is given the highest priority, while thread2 is given the lowest priority.

19. How to Terminate a Thread in C#


You cannot directly stop a thread in C#, but you can request its termination by using a flag or using the
Thread.Abort() method (deprecated).

Example: Terminating a Thread


using System; csharp
using System.Threading;

public class Program


{
static bool keepRunning = true;

static void Main()


{
Thread thread = new Thread(DoWork);
thread.Start();

Thread.Sleep(3000); // Simulate some work


keepRunning = false; // Request thread to stop
}

static void DoWork()


{
while (keepRunning)
{
Console.WriteLine("Working...");
Thread.Sleep(1000);
}
Console.WriteLine("Thread terminated.");
}
}

Explanation: A flag ( keepRunning ) is used to request the termination of the thread.

20. Inter-Thread Communication in C#


Inter-thread communication involves one thread sending a signal to another thread to notify it to continue execution.
Methods like Wait() , Pulse() , and Signal are used for this purpose.

21. How to Debug a Multi-threaded Application in C#


Debugging multithreaded applications requires special attention, as issues like race conditions and deadlocks can be
hard to reproduce.
Use Visual Studio Debugger: Allows you to inspect threads, set breakpoints, and step through multithreaded
code.
Analyze Thread States: Monitor thread state transitions and identify deadlocks or unresponsive threads.
These concepts and examples demonstrate how C# provides various tools to handle multithreading, synchronization,
and thread management, which are essential for building performant and responsive applications.
Notes on Arrays and Collections in C#
Understanding arrays and collections in C# is essential for effectively managing and manipulating data. Collections
provide more flexibility compared to arrays, and with generics, C# allows for more type safety and performance
improvements.

1. Arrays in C#
An array is a collection of elements of the same type, stored in contiguous memory locations. Arrays are useful for
fixed-size data storage.
Example: Declaring and Initializing an Array
int[] numbers = new int[5]; csharp
numbers[0] = 1;
numbers[1] = 2;
// and so on...

Key Points:
Arrays are zero-indexed.
They are fixed in size, meaning the size must be defined at creation and cannot change.
Can hold elements of the same data type.

2. 2D Arrays in C#
A 2D array is an array of arrays. It can be visualized as a matrix (rows and columns).
Example: Declaring and Initializing a 2D Array
int[,] matrix = new int[3, 3]; csharp
matrix[0, 0] = 1;
matrix[0, 1] = 2;
matrix[0, 2] = 3;

Key Points:
The first index represents the row and the second the column.
2D arrays are useful for representing grids, tables, or matrices.
3. Advantages and Disadvantages of Arrays in C#
Advantages:
Memory Efficiency: Arrays provide a contiguous block of memory, ensuring fast access to elements.
Performance: Accessing elements by index is fast.
Disadvantages:
Fixed Size: Once an array is initialized, its size cannot be changed, limiting flexibility.
Type Restrictions: Arrays can only store elements of a single type.

4. Collections in C#
Collections in C# are more flexible than arrays and come in various types, including ArrayList, Hashtable, and
Queue. Collections can dynamically resize and can store elements of different types (non-generic collections) or the
same type (generic collections).

5. ArrayList in C#
An ArrayList is a non-generic collection that can store elements of any type. It automatically resizes itself when more
elements are added.
Example: Using ArrayList
ArrayList list = new ArrayList(); csharp
list.Add(1);
list.Add("Hello");

Key Points:
It can store elements of different types.
It can dynamically grow in size.

6. Hashtable in C#
A Hashtable is a non-generic collection that stores key-value pairs. It allows fast retrieval of data based on a key.
Example: Using Hashtable
Hashtable hashtable = new Hashtable(); csharp
hashtable.Add(1, "Apple");
hashtable.Add(2, "Banana");

Key Points:
It stores key-value pairs.
Uses a hash function to store and retrieve data quickly.
The keys must be unique.

7. Non-Generic Stack in C#
A Stack is a non-generic collection that follows the LIFO (Last In, First Out) principle, meaning the last element added
is the first one to be removed.
Example: Using Stack
Stack stack = new Stack(); csharp
stack.Push(10);
stack.Push(20);
var item = stack.Pop(); // Removes 20

8. Non-Generic Queue in C#
A Queue is a non-generic collection that follows the FIFO (First In, First Out) principle.
Example: Using Queue
Queue queue = new Queue(); csharp
queue.Enqueue(10);
queue.Enqueue(20);
var item = queue.Dequeue(); // Removes 10

9. Non-Generic SortedList in C#
A SortedList is a non-generic collection that stores key-value pairs in sorted order according to the keys.
Example: Using SortedList
SortedList sortedList = new SortedList(); csharp
sortedList.Add(2, "Apple");
sortedList.Add(1, "Banana");

10. Advantages and Disadvantages of Non-Generic Collections in C#


Advantages:
Flexible: Can store elements of any type.
Resizing: Collections like ArrayList can resize dynamically.
Disadvantages:
Performance: Storing mixed types reduces performance due to type boxing and unboxing.
Lack of Type Safety: Non-generic collections don't enforce type safety, leading to potential runtime errors.

11. Generic Collections in C#


Generic collections allow you to define the type of elements they store, providing better type safety and
performance.

12. Generics in C#
Generics allow you to define classes, methods, and collections with a placeholder for data types, so you can use
them with any type while maintaining type safety.
Example: Using Generics
List<int> numbers = new List<int>(); csharp
numbers.Add(10);
numbers.Add(20);

Key Points:
Generics provide type safety.
They prevent boxing and unboxing overhead.
Commonly used in collections like List<T> , Dictionary<TKey, TValue> , etc.

13. Generic Constraints in C#


Generic constraints restrict the types that can be used with a generic class or method.
Example: Using Generic Constraints
public class MyClass<T> where T : class csharp
{
public T Data;
}

Key Points:
Constraints like where T : class enforce that T must be a reference type.

14. Generic List Collection in C#


The List collection is a dynamic array that can grow in size as elements are added.
Example: Using List
List<int> numbers = new List<int>(); csharp
numbers.Add(1);
numbers.Add(2);
numbers.Remove(1);

15. How to Sort a List of Complex Type in C#


You can sort a list of complex types by implementing IComparable or using a custom comparison delegate.
Example: Sorting a List of Complex Objects
public class Person : IComparable<Person> csharp
{
public string Name { get; set; }

public int CompareTo(Person other)


{
return this.Name.CompareTo(other.Name);
}
}

List<Person> people = new List<Person>


{
new Person { Name = "John" },
new Person { Name = "Alice" }
};

people.Sort(); // Sorts based on Name

16. Comparison Delegate in C#


A Comparison Delegate is a method that defines the criteria for comparing two objects, used in sorting.
Example: Using Comparison Delegate
List<int> numbers = new List<int> { 3, 1, 2 }; csharp
numbers.Sort((x, y) => y.CompareTo(x)); // Sort in descending order

17. Dictionary Collection Class in C#


A Dictionary<TKey, TValue> stores key-value pairs and allows fast lookup by key.
Example: Using Dictionary
Dictionary<int, string> dictionary = new Dictionary<int, string>(); csharp
dictionary.Add(1, "Apple");
dictionary.Add(2, "Banana");

18. Conversion Between ArrayList and Dictionary in C#


You can convert between ArrayList and Dictionary using Cast or loops.
Example: Converting ArrayList to Dictionary
ArrayList arrayList = new ArrayList() { 1, "Apple", 2, "Banana" }; csharp
Dictionary<int, string> dictionary = new Dictionary<int, string>();

for (int i = 0; i < arrayList.Count; i += 2)


{
dictionary.Add((int)arrayList[i], (string)arrayList[i + 1]);
}

19. List vs Dictionary in C#


List stores items in an ordered collection and allows duplicate elements.
Dictionary<TKey, TValue> stores key-value pairs and ensures fast retrieval using the key.
Comparison:
List is useful when you need an ordered collection without unique constraints on elements.
Dictionary<TKey, TValue> is ideal for when you need fast lookups by key.

20. Generic Stack Collection Class in C#


A Generic Stack follows the LIFO principle and is type-safe.
Example: Using Generic Stack
Stack<int> stack = new Stack<int>(); csharp
stack.Push(10);
stack.Push(20);
int popped = stack.Pop(); // 20

21. Generic Queue Collection Class in C#


A Generic Queue follows the FIFO principle and is type-safe.
Example: Using Generic Queue
Queue<int> queue = new Queue<int>(); csharp
queue.Enqueue(10);
queue.Enqueue(20);
int dequeued = queue.Dequeue(); // 10

22. Foreach Loop in C#


The foreach loop is used to iterate over collections like arrays, lists, and dictionaries.
Example: Using Foreach Loop
List<int> numbers = new List<int> { 1, 2, 3 }; csharp
foreach (var number in numbers)
{
Console.WriteLine(number);
}

23. Generic HashSet Collection Class in C#


A HashSet is a collection that stores unique elements and provides fast lookups.
Example: Using HashSet
HashSet<int> set = new HashSet<int>(); csharp
set.Add(1);
set.Add(2);
set.Add(2); // Duplicate will be ignored

24. Generic SortedList Collection Class in C#


A SortedList<TKey, TValue> stores key-value pairs in a sorted order based on the key.
Example: Using SortedList
SortedList<int, string> sortedList = new SortedList<int, string>(); csharp
sortedList.Add(1, "Apple");
sortedList.Add(2, "Banana");

25. Generic SortedSet Collection Class in C#


A SortedSet stores unique elements in sorted order.
Example: Using SortedSet
SortedSet<int> sortedSet = new SortedSet<int>(); csharp
sortedSet.Add(10);
sortedSet.Add(5);
sortedSet.Add(10); // Duplicate will be ignored

26. Generic SortedDictionary Collection Class in C#


A SortedDictionary<TKey, TValue> stores key-value pairs in sorted order by key.
Example: Using SortedDictionary
SortedDictionary<int, string> sortedDict = new SortedDictionary<int, string>(); csharp
sortedDict.Add(2, "Apple");
sortedDict.Add(1, "Banana");

27. Generic LinkedList Collection Class in C#


A LinkedList is a doubly linked list that allows efficient insertion and deletion of elements.
Example: Using LinkedList
LinkedList<int> linkedList = new LinkedList<int>(); csharp
linkedList.AddLast(10);
linkedList.AddLast(20);

28. Concurrent Collection in C#


Concurrent collections are thread-safe and allow safe access from multiple threads.
Examples include:
ConcurrentDictionary<TKey, TValue>
ConcurrentQueue
ConcurrentStack
ConcurrentBag

29. ConcurrentDictionary Collection Class in C#


A ConcurrentDictionary<TKey, TValue> allows safe multi-threaded access for adding, updating, and removing key-
value pairs.

30. ConcurrentQueue Collection Class in C#


A ConcurrentQueue provides thread-safe FIFO operations.

31. ConcurrentStack Collection Class in C#


A ConcurrentStack provides thread-safe LIFO operations.

32. ConcurrentBag Collection Class in C#


A ConcurrentBag is a thread-safe, unordered collection that allows multiple threads to add and take items.

33. BlockingCollection in C#
A BlockingCollection is a thread-safe collection that allows threads to wait for items to become available or to wait
until space is available to add items.
These concepts will help you handle data more efficiently, whether you're working with simple arrays or complex multi-
threaded applications with collections.

Notes on File Handling in C#


File handling is a key aspect of any application that needs to read from or write to files. In C#, the System.IO
namespace provides various classes to handle files and directories.

1. File Handling in C#
File handling in C# allows reading from and writing to files, manipulating file content, and handling file-related
operations such as creating, copying, deleting, and moving files.
Common Operations:
Create, open, and close files
Read and write text or binary data
Append to existing files
Check file existence
Delete files
C# provides a variety of classes to perform these tasks, such as FileStream , StreamReader , StreamWriter ,
File , etc.

2. FileStream Class in C#
The FileStream class provides a way to read from and write to files in a byte-oriented manner. It is useful when
working with binary files, large files, or for more efficient file I/O operations.
Key Methods of FileStream:
Read() : Reads bytes from a file.

Write() : Writes bytes to a file.

Seek() : Moves the cursor within the file to read or write data.

Close() : Closes the file stream.

Example: Using FileStream


using (FileStream fs = new FileStream("example.txt", FileMode.OpenOrCreate)) csharp
{
byte[] data = new byte[fs.Length];
fs.Read(data, 0, data.Length);
// Process data...
}
3. StreamReader and StreamWriter in C#
StreamReader is used for reading characters from a byte stream. It is primarily used for reading text files.
StreamWriter is used for writing characters to a byte stream. It is used for writing text files.

Key Methods of StreamReader:


Read() : Reads a single character from the stream.

ReadLine() : Reads a line of characters from the stream.

Close() : Closes the reader.

Key Methods of StreamWriter:


Write() : Writes data to the stream.

WriteLine() : Writes a line of text.

Close() : Closes the writer.

Example: Using StreamReader and StreamWriter


// Reading from a file using StreamReader csharp
using (StreamReader reader = new StreamReader("input.txt"))
{
string line = reader.ReadLine();
Console.WriteLine(line);
}

// Writing to a file using StreamWriter


using (StreamWriter writer = new StreamWriter("output.txt"))
{
writer.WriteLine("Hello, World!");
}

4. File Class in C#
The File class provides static methods for managing files, such as checking existence, copying, deleting, or
moving files. It simplifies the file operations without needing to create instances of file-related classes.
Key Methods of File:
Exists() : Checks if a file exists.

Create() : Creates a new file.

Delete() : Deletes a specified file.

Move() : Moves a file to a new location.

Copy() : Copies a file.


Example: Using File Class
// Checking if a file exists csharp
if (File.Exists("file.txt"))
{
Console.WriteLine("File exists!");
}

// Copying a file
File.Copy("source.txt", "destination.txt");

5. TextWriter and TextReader in C#


TextWriter is an abstract class that provides methods to write characters to a stream.
TextReader is an abstract class that provides methods to read characters from a stream.

Common Derived Classes:


StreamWriter (inherits from TextWriter )

StreamReader (inherits from TextReader )

These classes are ideal for handling character-based input/output for text files.

6. BinaryWriter and BinaryReader in C#


BinaryWriter is used to write primitive data types in binary format to a stream (such as integers, doubles,
etc.).
BinaryReader is used to read primitive data types from a binary stream.
Key Methods of BinaryWriter:
Write() : Writes a value to the stream.

Close() : Closes the writer.

Key Methods of BinaryReader:


Read() : Reads a primitive type.

Close() : Closes the reader.

Example: Using BinaryWriter and BinaryReader


// Writing data to a binary file csharp
using (BinaryWriter writer = new BinaryWriter(File.Open("data.bin", FileMode.Create)))
{
writer.Write(100);
writer.Write(3.14);
}

// Reading data from a binary file


using (BinaryReader reader = new BinaryReader(File.Open("data.bin", FileMode.Open)))
{
int intValue = reader.ReadInt32();
double doubleValue = reader.ReadDouble();
}

7. StringWriter and StringReader in C#


StringWriter is used to write to an in-memory string buffer.
StringReader is used to read from an in-memory string buffer.

These are useful when you need to manipulate text in memory before writing it to a file.
Example: Using StringWriter and StringReader
// Writing to a string using StringWriter csharp
using (StringWriter writer = new StringWriter())
{
writer.WriteLine("Hello, world!");
string result = writer.ToString();
}

// Reading from a string using StringReader


using (StringReader reader = new StringReader("Hello, world!"))
{
string line = reader.ReadLine();
Console.WriteLine(line);
}

8. FileInfo Class in C#
The FileInfo class provides instance methods to work with files, such as getting file information, copying,
deleting, and moving files.
Key Methods of FileInfo:
CopyTo() : Copies the file to a new location.

Delete() : Deletes the file.

Exists() : Checks if the file exists.

Length : Gets the size of the file.


LastWriteTime : Gets the last write time of the file.
Example: Using FileInfo
FileInfo file = new FileInfo("example.txt"); csharp
if (file.Exists)
{
Console.WriteLine("File size: " + file.Length);
}

9. DirectoryInfo Class in C#
The DirectoryInfo class provides instance methods to work with directories, such as creating, deleting, or
renaming directories.
Key Methods of DirectoryInfo:
Create() : Creates the directory.

Delete() : Deletes the directory.

GetFiles() : Gets an array of files in the directory.

GetDirectories() : Gets an array of subdirectories.

Example: Using DirectoryInfo


DirectoryInfo directory = new DirectoryInfo(@"C:\Temp"); csharp
if (!directory.Exists)
{
directory.Create();
}

10. Export and Import Excel Data in C#


In C#, you can use libraries such as ClosedXML or EPPlus to export and import data to and from Excel files.
Example: Using ClosedXML for Exporting Data to Excel
using ClosedXML.Excel; csharp

var workbook = new XLWorkbook();


var worksheet = workbook.Worksheets.Add("Sheet1");
worksheet.Cell("A1").Value = "Hello, Excel!";
workbook.SaveAs("example.xlsx");

Example: Importing Data from Excel using ClosedXML


var workbook = new XLWorkbook("example.xlsx"); csharp
var worksheet = workbook.Worksheet(1);
string value = worksheet.Cell("A1").Value.ToString();
Console.WriteLine(value);

Common Libraries for Excel Operations:


EPPlus: Great for reading/writing Excel files without needing Excel installed.
ClosedXML: Easy-to-use library for Excel files, based on Open XML SDK.

Conclusion
C# provides powerful tools for managing file operations with various classes designed for both text and binary data.
The StreamReader and StreamWriter classes are ideal for text-based file I/O, while the BinaryReader and
BinaryWriter classes handle binary data. For advanced file manipulation, you can use classes like FileInfo
and DirectoryInfo . Additionally, exporting and importing Excel data can be efficiently managed using libraries like
ClosedXML or EPPlus.

Notes on Concurrency in C#
Concurrency refers to the ability of a program to handle multiple tasks or operations simultaneously. C# provides
robust support for concurrency using features like async , await , Task , and the Task Parallel Library
(TPL) , making it easier to manage multithreading and asynchronous programming.

1. Introduction to Concurrency
Concurrency allows a program to execute multiple operations at the same time. While true parallelism involves
executing multiple threads simultaneously, concurrency enables a program to manage multiple tasks efficiently. In C#,
concurrency is commonly achieved using:
Asynchronous programming (using async and await )
Task Parallel Library (TPL) for managing threads and tasks
Parallel LINQ (PLINQ) for data parallelism
C# makes concurrent programming easier with asynchronous programming, which allows non-blocking operations.

2. Async and Await in C#


async and await are keywords that enable asynchronous programming in C#. They simplify the process of
writing asynchronous code, allowing you to execute long-running operations without blocking the main thread.
async : Used to define an asynchronous method.

await : Used to wait for the result of an asynchronous operation without blocking the thread.
Example: Async and Await
public async Task<int> GetDataAsync() csharp
{
await Task.Delay(1000); // Simulates a delay (e.g., reading data)
return 42;
}

public async void Run()


{
int result = await GetDataAsync();
Console.WriteLine(result); // Prints: 42
}

3. Task in C#
A Task represents an asynchronous operation. It is the foundation of asynchronous programming in C# and is used
for operations that execute in the background without blocking the main thread.
Key Methods of Task:
Task.Run() : Creates and starts a task.

Task.WhenAll() : Waits for all tasks to complete.

Task.WhenAny() : Waits for any one task to complete.

Example: Using Task


Task task = Task.Run(() => Console.WriteLine("Hello from Task")); csharp
await task;

4. How to Return a Value from Task in C#


A Task<T> is used to represent an asynchronous operation that returns a result. To return a value from a task, you
can specify the return type when creating the task.
Example: Task Returning a Value
public async Task<int> GetNumberAsync() csharp
{
await Task.Delay(1000);
return 42;
}

public async void Run()


{
int result = await GetNumberAsync();
Console.WriteLine(result); // Prints: 42
}

5. How to Execute Multiple Tasks in C#


You can execute multiple tasks concurrently using the Task.WhenAll() method, which waits for all tasks to
complete.
Example: Running Multiple Tasks
public async Task RunMultipleTasks() csharp
{
Task task1 = Task.Run(() => Console.WriteLine("Task 1"));
Task task2 = Task.Run(() => Console.WriteLine("Task 2"));
await Task.WhenAll(task1, task2);
}

6. How to Limit Number of Concurrent Tasks in C#


To limit the number of concurrent tasks, use the SemaphoreSlim class or the Task.WhenAny() method to
control concurrency.
Example: Limiting Concurrent Tasks
SemaphoreSlim semaphore = new SemaphoreSlim(3); // Limit to 3 concurrent tasks csharp

public async Task RunLimitedTasks()


{
var tasks = new List<Task>();
for (int i = 0; i < 10; i++)
{
await semaphore.WaitAsync();
tasks.Add(Task.Run(async () =>
{
try
{
Console.WriteLine($"Task {i} running...");
await Task.Delay(1000); // Simulate work
}
finally
{
semaphore.Release();
}
}));
}
await Task.WhenAll(tasks);
}

7. How to Cancel a Task in C# using Cancellation Token


A CancellationToken allows you to cancel a task or operation asynchronously.
CancellationTokenSource : Creates a token that can be passed to tasks.

Cancel() : Cancels the operation associated with the token.

Example: Cancelling a Task


CancellationTokenSource cts = new CancellationTokenSource(); csharp
CancellationToken token = cts.Token;

public async Task RunTaskWithCancellation(CancellationToken token)


{
await Task.Delay(5000, token); // Task will be cancelled if token is triggered
Console.WriteLine("Task completed");
}

public void CancelTask()


{
cts.Cancel(); // Cancel the task
}

8. How to Create Synchronous Method using Task in C#


You can use Task.Run to execute a synchronous method asynchronously.
Example: Synchronous Method with Task
public void SyncMethod() csharp
{
Console.WriteLine("Synchronous method.");
}

public async Task RunAsync()


{
await Task.Run(() => SyncMethod());
}
9. Retry Pattern in C#
The retry pattern is used to attempt an operation multiple times in case of failure, with a delay between attempts.
Example: Retry Pattern
public async Task RetryOperationAsync(int maxRetries) csharp
{
int attempt = 0;
while (attempt < maxRetries)
{
try
{
await Task.Run(() => Console.WriteLine("Attempting operation..."));
break; // Exit loop if successful
}
catch
{
attempt++;
await Task.Delay(1000); // Wait before retrying
}
}
}

10. Only One Pattern in C#


The only one pattern ensures that only a single instance of a task or operation is executed at any time.
Example: Ensuring Only One Task
private static bool isRunning = false; csharp

public async Task OnlyOneAsync()


{
if (isRunning)
return;

try
{
isRunning = true;
await Task.Delay(1000); // Simulating work
}
finally
{
isRunning = false;
}
}
11. How to Control the Result of a Task in C#
You can control the result of a task using Task<TResult> and await .
Example: Controlling Task Result
public async Task<int> GetNumberAsync() csharp
{
await Task.Delay(1000); // Simulating a delay
return 42;
}

public async void Run()


{
int result = await GetNumberAsync();
Console.WriteLine(result); // Prints: 42
}

12. Task-Based Asynchronous Programming in C#


Task-based asynchronous programming uses Task and async/await to manage non-blocking operations.
Task.WhenAll() and Task.WhenAny() are used for waiting on multiple tasks.

async methods return a Task or Task<TResult> , allowing for asynchronous operations without blocking
the calling thread.

13. Chaining Tasks by Using Continuation Tasks


Continuation tasks allow you to execute a task after another task completes. This is useful when you need to perform
an action based on the result of a previous task.
Example: Chaining Tasks
public Task<int> GetNumberAsync() csharp
{
return Task.Run(() => 42);
}

public async void Run()


{
int result = await GetNumberAsync().ContinueWith(task => task.Result * 2);
Console.WriteLine(result); // Prints: 84
}
14. How to Attach Child Tasks to a Parent Task in C#
You can attach child tasks to a parent task using the ContinueWith method or using Task.WhenAny and
Task.WhenAll for coordination.

Example: Parent and Child Tasks


public async Task RunParentAndChild() csharp
{
var parentTask = Task.Run(() => "Parent task");
var childTask = parentTask.ContinueWith((task) => "Child task");

await Task.WhenAll(parentTask, childTask);


}

15. ValueTask in C#
ValueTask is a structure that represents a task that may or may not be completed synchronously. It helps optimize
performance by reducing memory allocations in scenarios where tasks are already completed synchronously.
Example: Using ValueTask
public ValueTask<int> GetNumberAsync() csharp
{
return new ValueTask<int>(42); // Already completed
}

16. How to Cancel a Non-Cancellable Task in C#


It’s not always possible to cancel a non-cancellable task. In such cases, you can use timeout mechanisms or
periodically check for cancellation and terminate the task manually.

17. Asynchronous Streams in C#


Asynchronous streams allow asynchronous iteration over collections of data, like reading lines from a file or
receiving data from a network.
Use the async keyword with IEnumerable to create asynchronous streams.
Example: Asynchronous Streams
public async IAsyncEnumerable<int> GetNumbersAsync() csharp
{
for (int i = 0; i < 5; i++)
{
await Task.Delay(1000);
yield return i;
}
}

public async void Run()


{
await foreach (var number in GetNumbersAsync())
{
Console.WriteLine(number);
}
}

18. How to Cancel Asynchronous Stream in C#


You can cancel asynchronous streams using a CancellationToken passed to the stream.
Example: Cancelling Async Stream
public async IAsyncEnumerable<int> GetNumbersAsync(CancellationToken token) csharp
{
for (int i = 0; i < 5; i++)
{
token.ThrowIfCancellationRequested();
await Task.Delay(1000);
yield return i;
}
}

public async void Run()


{
var cts = new CancellationTokenSource();
var token = cts.Token;

try
{
await foreach (var number in GetNumbersAsync(token))
{
Console.WriteLine(number);
}
}
catch (OperationCanceledException)
{
Console.WriteLine("Stream was cancelled");
}
}
Conclusion
Concurrency in C# offers powerful tools for asynchronous programming and multi-tasking. Using async / await ,
Task , CancellationToken , and related concepts, developers can manage complex tasks, enhance performance,
and handle multiple operations without blocking the UI thread. From basic async methods to advanced features like
asynchronous streams and task chaining, these tools make C# an excellent choice for modern, high-performance
applications.

Notes on Task Parallel Library (TPL) in C#


The Task Parallel Library (TPL) in C# is a set of public types and APIs in the System.Threading.Tasks
namespace that provide a simplified and efficient way to write parallel and asynchronous code. It allows developers to
work with tasks, manage concurrency, and scale operations across multiple processors.

1. Task Parallel Library in C#


The Task Parallel Library (TPL) is designed to work with multiple processors and helps in handling tasks
asynchronously and in parallel. It provides easier and more efficient ways to write multithreaded and parallel code. The
core types in TPL include Task , Task<T> , Parallel , and Parallel.For .
Task represents an asynchronous operation.

Parallel provides methods for parallel execution, such as For , ForEach , and Invoke .

Task.WhenAll() and Task.WhenAny() help manage multiple tasks.

Advantages of TPL:
Scalable: Automatically adjusts the number of threads based on system capabilities.
Flexible: Supports both synchronous and asynchronous execution.
Easier error handling: Centralized handling of exceptions from parallel tasks.

2. Parallel For in C#
The Parallel.For method is used to execute a for loop in parallel, breaking the iterations into multiple threads. It
helps speed up operations that are CPU-bound by utilizing multiple processors.
Example: Parallel For
Parallel.For(0, 10, i => csharp
{
Console.WriteLine($"Task {i} is running on thread {Thread.CurrentThread.ManagedThre
adId}");
});

How it works:
Each iteration of the loop is executed by a different thread.
The iterations are processed concurrently, speeding up the execution of the loop.
Use Cases:
Data processing where each iteration is independent.
Tasks like image processing or scientific computations that can be parallelized.

3. Parallel Foreach Loop in C#


Parallel.ForEach is used to process each item of a collection concurrently. It can be applied to arrays, lists, or
other IEnumerable types.
Example: Parallel Foreach
var numbers = new List<int> { 1, 2, 3, 4, 5 }; csharp
Parallel.ForEach(numbers, number =>
{
Console.WriteLine($"Processing {number} on thread {Thread.CurrentThread.ManagedThre
adId}");
});

How it works:
Iterates over the collection in parallel, processing each element concurrently.
Each element of the collection can be handled by a separate thread.
Use Cases:
Processing collections such as large datasets.
Simultaneous updates of elements in a collection.

4. Parallel Invoke in C#
Parallel.Invoke executes multiple actions concurrently. Unlike Parallel.For or Parallel.ForEach , it is
used when you have a set of independent actions to execute.
Example: Parallel Invoke
Parallel.Invoke( csharp
() => Console.WriteLine("Task 1 is running"),
() => Console.WriteLine("Task 2 is running"),
() => Console.WriteLine("Task 3 is running")
);

How it works:
Executes each action in parallel.
Useful when you have multiple independent tasks that don't require a loop or collection.
Use Cases:
Simultaneous execution of multiple independent actions (e.g., downloading multiple files or processing
independent tasks).

5. Maximum Degree of Parallelism in C#


The Maximum Degree of Parallelism (MaxDegreeOfParallelism) specifies the maximum number of tasks that can
run concurrently in a parallel loop or operation. This can be controlled to prevent overloading the CPU or other system
resources.
Example: Setting Max Degree of Parallelism
Parallel.For(0, 100, new ParallelOptions { MaxDegreeOfParallelism = 4 }, i => csharp
{
Console.WriteLine($"Task {i} is running on thread {Thread.CurrentThread.ManagedThre
adId}");
});

How it works:
The MaxDegreeOfParallelism property limits the number of threads that can run in parallel.
If the number exceeds this limit, tasks are queued and executed later.
Use Cases:
Controlling system resource usage, especially in scenarios with high CPU or memory consumption.

6. How to Cancel Parallel Operations in C#


You can cancel parallel operations using a CancellationToken . The Parallel.For and Parallel.ForEach
methods accept a ParallelOptions parameter, which includes a CancellationToken .
Example: Cancel Parallel Operations
CancellationTokenSource cts = new CancellationTokenSource(); csharp
ParallelOptions options = new ParallelOptions { CancellationToken = cts.Token };

try
{
Parallel.For(0, 100, options, i =>
{
if (cts.Token.IsCancellationRequested)
{
Console.WriteLine("Operation cancelled");
return;
}
Console.WriteLine($"Task {i} is running");
});
}
catch (OperationCanceledException)
{
Console.WriteLine("Parallel operation was canceled");
}

How it works:
You initiate the cancellation by calling cts.Cancel() .
The tasks regularly check for cancellation and can exit gracefully if requested.

7. Atomic Methods, Thread Safety, and Race Conditions in C#


Atomic methods ensure that an operation is completed without interference from other threads.
Thread safety ensures that shared data is accessed or modified in a way that avoids data corruption or race
conditions.
Race conditions occur when two threads access shared data concurrently, and the final result depends on the
timing of their execution.
Example: Thread Safety using Interlocked
int counter = 0; csharp

Parallel.For(0, 1000, i =>


{
Interlocked.Increment(ref counter); // Atomic increment
});

Console.WriteLine($"Counter: {counter}"); // Expected output: 1000

How it works:
The Interlocked class provides atomic methods (like Increment ) that perform operations on variables in
a thread-safe manner.

8. Interlocked vs Lock in C#
Interlocked provides atomic operations on variables shared by multiple threads (e.g., Increment ,
CompareExchange ).

lock (also known as Monitor ) is used to ensure that only one thread can access a critical section of code at
a time.
Example: Using lock
private static readonly object _lock = new object(); csharp
private static int counter = 0;

public void IncrementCounter()


{
lock (_lock)
{
counter++;
}
}

Difference:
Interlocked: Provides a simple and efficient way to perform atomic operations.
Lock: Provides a mechanism for mutual exclusion, ensuring only one thread executes a critical section at a time.

9. Parallel LINQ (PLINQ) in C#


Parallel LINQ (PLINQ) is a parallel implementation of LINQ that allows you to run LINQ queries in parallel. It can be
used for CPU-bound operations that involve processing collections.
Example: Parallel LINQ
var numbers = Enumerable.Range(1, 1000); csharp
var evenNumbers = numbers.AsParallel().Where(x => x % 2 == 0).ToArray();

How it works:
The AsParallel method allows you to execute the LINQ query in parallel.
PLINQ automatically distributes the work across multiple threads.
Use Cases:
CPU-bound operations such as filtering or transforming large collections.
Processing data concurrently without manually managing threads.

10. Multithreading vs Asynchronous Programming vs Parallel Programming in C#


Multithreading: Involves running multiple threads in parallel. Each thread executes a part of the program
concurrently. It is suitable for CPU-bound operations.
Asynchronous Programming: Involves executing tasks asynchronously (non-blocking), typically for I/O-bound
operations like file reading, network calls, etc. It doesn't necessarily use multiple threads but allows the thread to
handle other tasks while waiting.
Parallel Programming: Refers to executing multiple operations concurrently, utilizing multiple processors or
cores to improve performance, mainly for CPU-bound operations.
Key Differences:
Multithreading: Explicitly manages multiple threads.
Asynchronous Programming: Does not block threads but performs operations asynchronously.
Parallel Programming: Splits work into multiple tasks that are processed concurrently, typically across multiple
processors.
Example of Multithreading:
Thread thread = new Thread(() => Console.WriteLine("Multithreaded")); csharp
thread.Start();

Example of Asynchronous Programming:


public async Task<string> GetDataAsync() csharp
{
await Task.Delay(1000); // Simulating I/O operation
return

"Data fetched"; }

#### **Example of Parallel Programming:**


```csharp
Parallel.For(0, 1000, i => Console.WriteLine($"Task {i}"));

These topics offer a comprehensive understanding of the Task Parallel Library (TPL), parallel operations, and their
interplay with multithreading and asynchronous programming in C#. They provide the tools for efficient, high-
performance, and scalable applications in C#.

Notes on AutoMapper in C#
AutoMapper is a popular object-to-object mapping library that helps in mapping one object type to another, typically
used in scenarios where you need to transfer data between objects with similar structures. It reduces the amount of
repetitive code for copying data between objects, particularly in DTOs (Data Transfer Objects) and domain models.

1. AutoMapper in C#
AutoMapper is a library that simplifies object-to-object mapping, eliminating the need to write manual mapping code.
It automatically maps properties from one object to another based on matching property names and types.
Setup: You define mappings between the source and destination types once.
Execution: You use the Mapper.Map<TSource, TDestination>() method to map the properties.
Install AutoMapper via NuGet:
Install-Package AutoMapper bash

Basic Example:
using AutoMapper; csharp

public class Source


{
public int Id { get; set; }
public string Name { get; set; }
}

public class Destination


{
public int Id { get; set; }
public string Name { get; set; }
}

public class Program


{
public static void Main()
{
var config = new MapperConfiguration(cfg => cfg.CreateMap<Source, Destination>
());
IMapper mapper = config.CreateMapper();

Source source = new Source { Id = 1, Name = "AutoMapper" };


Destination dest = mapper.Map<Destination>(source);

Console.WriteLine($"Id: {dest.Id}, Name: {dest.Name}");


}
}

How it works:
AutoMapper automatically maps properties from the source ( Source ) to the destination ( Destination ), as
long as the property names and types match.

2. AutoMapper Complex Mapping in C#


Complex Mapping refers to mapping properties that are more than simple scalar types. This could include mapping
between nested objects, collections, or properties that require transformations during mapping.
Example: Complex Mapping
public class Address csharp
{
public string Street { get; set; }
public string City { get; set; }
}

public class User


{
public string Name { get; set; }
public Address Address { get; set; }
}

public class UserDTO


{
public string Name { get; set; }
public string FullAddress { get; set; }
}

var config = new MapperConfiguration(cfg =>


{
cfg.CreateMap<User, UserDTO>()
.ForMember(dest => dest.FullAddress, opt => opt.MapFrom(src => $"{src.Address.St
reet}, {src.Address.City}"));
});

IMapper mapper = config.CreateMapper();


User user = new User { Name = "John", Address = new Address { Street = "123 Main St", C
ity = "Springfield" } };
UserDTO userDTO = mapper.Map<UserDTO>(user);

Console.WriteLine(userDTO.FullAddress); // Output: 123 Main St, Springfield

How it works:
In this example, Address is a complex type, and we map the FullAddress to a combination of two
properties ( Street and City ) in UserDTO .
The ForMember method is used to define custom mapping rules.

3. How to Map Complex Type to Primitive Type using AutoMapper in C#


AutoMapper can also map complex types to primitive types (like string , int , bool , etc.), using custom logic
for mapping the complex object.
Example: Mapping Complex to Primitive
public class Person csharp
{
public string FirstName { get; set; }
public string LastName { get; set; }
}

public class PersonDTO


{
public string FullName { get; set; }
}

var config = new MapperConfiguration(cfg =>


{
cfg.CreateMap<Person, PersonDTO>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => $"{src.FirstName} {s
rc.LastName}"));
});

IMapper mapper = config.CreateMapper();


Person person = new Person { FirstName = "John", LastName = "Doe" };
PersonDTO personDTO = mapper.Map<PersonDTO>(person);

Console.WriteLine(personDTO.FullName); // Output: John Doe

How it works:
Here, Person is a complex type, and PersonDTO is a primitive type that combines the FirstName and
LastName fields into a single string.

4. AutoMapper Reverse Mapping in C#


Reverse Mapping allows you to automatically create a reverse mapping configuration, i.e., mapping from
Destination back to Source .

Example: Reverse Mapping


var config = new MapperConfiguration(cfg => csharp
{
cfg.CreateMap<Source, Destination>();
cfg.CreateMap<Destination, Source>(); // Reverse Mapping
});

IMapper mapper = config.CreateMapper();

Destination dest = new Destination { Id = 1, Name = "AutoMapper" };


Source source = mapper.Map<Source>(dest);

Console.WriteLine($"Id: {source.Id}, Name: {source.Name}"); // Output: Id: 1, Name: Au


toMapper

How it works:
The reverse map can be defined with cfg.CreateMap<Destination, Source>() .
This eliminates the need for writing custom mapping code when needing to map in the opposite direction.

5. AutoMapper Conditional Mapping in C#


Conditional Mapping allows you to map properties only when certain conditions are met. This is useful for mapping
only non-null or specific values.
Example: Conditional Mapping
public class Employee csharp
{
public string Name { get; set; }
public int? Age { get; set; }
}

public class EmployeeDTO


{
public string Name { get; set; }
public string Age { get; set; }
}

var config = new MapperConfiguration(cfg =>


{
cfg.CreateMap<Employee, EmployeeDTO>()
.ForMember(dest => dest.Age, opt => opt.Condition(src => src.Age.HasValue))
.ForMember(dest => dest.Age, opt => opt.MapFrom(src => src.Age.Value.ToString
()));
});

IMapper mapper = config.CreateMapper();


Employee employee = new Employee { Name = "John", Age = 30 };
EmployeeDTO employeeDTO = mapper.Map<EmployeeDTO>(employee);

Console.WriteLine(employeeDTO.Age); // Output: 30

How it works:
We used Condition to map Age only if it's not null.

6. AutoMapper Ignore Method in C#


The Ignore method allows you to skip certain properties during the mapping process. This is useful when you do
not want to map some properties of the destination object.
Example: Ignore Mapping
var config = new MapperConfiguration(cfg => csharp
{
cfg.CreateMap<Source, Destination>()
.ForMember(dest => dest.Name, opt => opt.Ignore());
});

IMapper mapper = config.CreateMapper();


Source source = new Source { Id = 1, Name = "John" };
Destination dest = mapper.Map<Destination>(source);

Console.WriteLine(dest.Name); // Output: null (Name is ignored)

How it works:
The ForMember method with Ignore ensures that Name is not mapped from Source to Destination .

7. Fixed and Dynamic Values in Destination Property in AutoMapper


AutoMapper allows you to set fixed or dynamic values in the destination properties while mapping.
Fixed Values: Set the destination property to a constant value.
Dynamic Values: You can calculate or derive the destination value based on the source.
Example: Fixed and Dynamic Values
public class Product csharp
{
public string Name { get; set; }
public decimal Price { get; set; }
}

public class ProductDTO


{
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}

var config = new MapperConfiguration(cfg =>


{
cfg.CreateMap<Product, ProductDTO>()
.ForMember(dest => dest.Category, opt => opt.MapFrom(src => "Electronics")) //
Fixed value
.ForMember(dest => dest.Price, opt => opt.MapFrom(src => src.Price * 1.1m)); //
Dynamic value
});

IMapper mapper = config.CreateMapper();


Product product = new Product { Name = "Laptop", Price = 1000 };
ProductDTO productDTO = mapper.Map<ProductDTO>(product);

Console.WriteLine($"Category: {productDTO.Category}, Price: {productDTO.Price}"); // O


utput: Category: Electronics, Price: 1100

How it works:
The Category is set to a fixed value ( "Electronics" ).
The Price is calculated dynamically based on the Price property of Product .

Conclusion:
AutoMapper in
C# is a powerful and efficient tool for mapping between objects, handling everything from simple properties to
complex transformations and mappings. Understanding the basics like reverse mapping, conditional mapping, and
ignoring properties is essential for using AutoMapper effectively in production-level applications.

Notes on Optional Parameters, Indexers, and Enums in C#


These are essential topics in C# that help streamline your code, improve readability, and enhance flexibility when
dealing with methods, data structures, and types.

1. How to Make Optional Parameters in C#


In C#, optional parameters allow you to define method parameters with default values. This provides flexibility by
allowing you to omit arguments when calling a method.
Optional parameters must be placed at the end of the parameter list.
You can specify a default value for the parameter, which will be used if no argument is provided during the
method call.
Syntax:
public returnType MethodName(paramType paramName = defaultValue) csharp

Example:
using System; csharp

class Program
{
public static void Greet(string name = "Guest", string greeting = "Hello")
{
Console.WriteLine($"{greeting}, {name}!");
}

static void Main()


{
// Calling the method with both arguments
Greet("John", "Good Morning"); // Output: Good Morning, John!

// Calling the method with one argument, uses default for the second one
Greet("Alice"); // Output: Hello, Alice!

// Calling the method with no arguments, uses defaults for both


Greet(); // Output: Hello, Guest!
}
}

How it works:
In the Greet method, name defaults to "Guest" and greeting defaults to "Hello" . You can skip
parameters during the method call, and the default values will be used.

2. Indexers in C#
An indexer is a special type of property in C# that allows instances of a class or struct to be indexed like arrays or
collections. Indexers provide a way to access data in a collection, array, or object in a more intuitive way.
Syntax:
public returnType this[parameterType index] csharp
{
get { ... }
set { ... }
}

this[index] is the indexer declaration.


The index can be any valid type (int, string, etc.), and the method can define logic to retrieve or set values based
on the index.
Example:
using System; csharp

class MyCollection
{
private int[] numbers = new int[10];

// Indexer to access the elements of the array


public int this[int index]
{
get
{
if (index >= 0 && index < numbers.Length)
return numbers[index];
else
throw new IndexOutOfRangeException("Index out of range");
}
set
{
if (index >= 0 && index < numbers.Length)
numbers[index] = value;
else
throw new IndexOutOfRangeException("Index out of range");
}
}
}

class Program
{
static void Main()
{
MyCollection collection = new MyCollection();

// Using the indexer to set values


collection[0] = 10;
collection[1] = 20;

// Using the indexer to get values


Console.WriteLine(collection[0]); // Output: 10
Console.WriteLine(collection[1]); // Output: 20
}
}

How it works:
The MyCollection class defines an indexer this[int index] , allowing access to the underlying
numbers array using index notation ( collection[0] ).

The get and set methods are used to retrieve and assign values.

3. Indexers Real-Time Example in C#


Indexers are useful for working with custom collections, dictionaries, or data structures. They enable objects of these
types to be accessed as though they were arrays.
Example: Implementing a Custom String Dictionary using Indexers
using System; csharp
using System.Collections.Generic;
class StringDictionary
{
private Dictionary<string, string> dictionary = new Dictionary<string, string>();

// Indexer to access the dictionary


public string this[string key]
{
get
{
if (dictionary.ContainsKey(key))
return dictionary[key];
else
throw new KeyNotFoundException($"Key '{key}' not found");
}
set
{
dictionary[key] = value;
}
}
}

class Program
{
static void Main()
{
StringDictionary myDict = new StringDictionary();

// Using the indexer to add items


myDict["name"] = "Alice";
myDict["age"] = "25";

// Using the indexer to retrieve items


Console.WriteLine(myDict["name"]); // Output: Alice
Console.WriteLine(myDict["age"]); // Output: 25

// Handling non-existent key


try
{
Console.WriteLine(myDict["address"]); // Throws exception
}
catch (KeyNotFoundException e)
{
Console.WriteLine(e.Message); // Output: Key 'address' not found
}
}
}

How it works:
The StringDictionary class uses an indexer to allow access to a private dictionary ( dictionary ) via the
this[string key] syntax.
This approach makes the dictionary more intuitive and easy to use, similar to an array or dictionary.

4. Enums in C#
An enum (short for enumeration) is a distinct type that defines a set of named constants representing integral
values. Enums provide meaningful names to represent data values, improving code readability and maintainability.
Syntax:
public enum EnumName csharp
{
Value1,
Value2,
Value3,
...
}

By default, the underlying type of an enum is int , and each value starts from 0 and increments by 1.
You can also explicitly assign values to the enum members.
Example:
using System; csharp

enum Day
{
Sunday, // 0
Monday, // 1
Tuesday, // 2
Wednesday, // 3
Thursday, // 4
Friday, // 5
Saturday // 6
}

class Program
{
static void Main()
{
Day today = Day.Monday;
Console.WriteLine(today); // Output: Monday

// Enum to integer conversion


int dayNumber = (int)today;
Console.WriteLine(dayNumber); // Output: 1

// Integer to Enum conversion


Day day = (Day)3;
Console.WriteLine(day); // Output: Wednesday
}
}

How it works:
The Day enum defines days of the week, with default integer values starting from 0.
Enums are strongly typed, meaning you can safely use them instead of magic numbers.
Example: Enum with Explicit Values
enum Day csharp
{
Sunday = 1,
Monday = 2,
Tuesday = 3,
Wednesday = 4,
Thursday = 5,
Friday = 6,
Saturday = 7
}

How it works:
The values of the Day enum are explicitly set, so Sunday starts at 1, Monday at 2, and so on.

Key Benefits of Using Enums:


1. Code Clarity: Enums improve the readability of your code by replacing magic numbers or strings with
descriptive names.
2. Type Safety: You can ensure that only valid values are assigned to variables that are of enum type.
3. Maintainability: Adding new values to an enum can be done without breaking the existing code.

Conclusion:
Optional Parameters in C# are a convenient feature that allows you to provide default values for parameters in
methods, reducing the need for method overloading.
Indexers provide a way to treat an object like an array, which is useful for custom collections and other data
structures.
Enums enhance code readability and maintainability by allowing you to define meaningful names for integral
values, instead of using raw numbers.
By leveraging these features, you can write cleaner, more flexible, and more maintainable code in C#.
Here’s a more detailed breakdown of the .NET Framework concepts, with enhanced explanations and examples for
each topic:

1. DOT NET Framework


The .NET Framework is a comprehensive platform developed by Microsoft for building, deploying, and running
applications. It consists of:
Common Language Runtime (CLR) for executing code.
.NET Class Library for reusable types.
ASP.NET, Windows Forms, WPF, and ADO.NET for building web, desktop, and data-driven applications.
Example:
// C# code example running under the .NET Framework csharp
Console.WriteLine("Hello, .NET Framework!");

This code uses the Console class from the .NET Class Library to print output to the console.

2. Common Language Runtime (CLR) in .NET Framework


The CLR is the runtime environment of the .NET Framework that manages the execution of .NET programs. Key
features:
Memory Management (Automatic Garbage Collection)
Code Execution (JIT Compilation)
Exception Handling (Consistent across .NET languages)
Type Safety (Ensures safe operations on data types)
Example:
// Example of CLR's Memory Management csharp
int[] numbers = new int[10];
// The CLR automatically manages memory allocation and deallocation.

In this example, the CLR allocates memory for the numbers array, and once it's no longer in use, the garbage
collector will free the memory automatically.

3. .NET Program Execution Process


The steps for executing a .NET program:
1. Source Code: Written in C#, VB.NET, etc.
2. Compilation: Code is compiled into Intermediate Language (IL).
3. Assembly: The IL code is stored in an assembly (DLL/EXE).
4. JIT Compilation: At runtime, CLR compiles IL into native code.
5. Execution: CLR manages the execution and garbage collection.
Example:
Source Code: Written in C#
public class Program { csharp
public static void Main() {
Console.WriteLine("Hello, .NET!");
}
}

Compilation: The C# code is compiled into IL code.


Assembly: This IL code is stored in an EXE or DLL file.
JIT Compilation: When you run the application, the CLR JIT compiles the IL code into native machine code for
your processor.

4. Intermediate Language (IL) Code in C#


IL Code is platform-independent and intermediate code produced after compiling source code. It is executed by the
CLR.
Example:
Source Code (C#):
int a = 5; csharp
int b = 10;
int result = a + b;
Console.WriteLine(result);

IL Code: The IL code generated would look something like:


ldc.i4.s 5 il
ldc.i4.s 10
add
call void [System.Console]System.Console::WriteLine(int32)

You can view this code using the ILDASM tool.


ildasm MyApp.exe bash

This will show you the IL code for your C# application.

5. Common Type System (CTS) in .NET Framework


The CTS defines the types that are used across all .NET languages. It ensures that types defined in different
languages are compatible.
Value Types: Types that hold the actual data (e.g., int , char , struct ).
Reference Types: Types that hold references to data (e.g., string , array , class ).
Example:
// Value Type Example csharp
int a = 10;
int b = a; // b gets a copy of a's value

// Reference Type Example


string s1 = "Hello";
string s2 = s1; // s2 references the same object as s1

In this example, a and b are value types, so they hold separate copies of the value. s1 and s2 are reference
types, so they both point to the same string object.

6. Common Language Specification (CLS) in .NET Framework


The CLS is a set of rules ensuring that code written in different languages can interact seamlessly. It restricts the use
of certain language features for better compatibility.
For example, C# allows unsigned types ( uint , ulong ), but these are not part of the CLS, so they cannot be used
in other languages like VB.NET.
Example:
// CLS-Compliant Code csharp
public class MyClass {
public int MyMethod() {
return 42;
}
}

Here, using int ensures the code is CLS-compliant, as int is supported by all .NET languages.
7. Managed and Unmanaged Code in .NET Framework
Managed Code: Code executed by the CLR with automatic memory management.
Unmanaged Code: Code executed directly by the operating system, requiring manual memory management (like
C++).
Example:
Managed Code (C#):
int[] arr = new int[10]; // Managed by CLR csharp

Unmanaged Code (C++):


int* arr = new int[10]; // Programmer manages memory cpp

In managed code, memory is automatically allocated and deallocated by the CLR's garbage collector.

8. Assembly (DLL and EXE) in .NET Framework


Assemblies in .NET contain compiled code and metadata. They can be of two types:
DLL (Dynamic Link Library): A collection of reusable classes or methods.
EXE (Executable): A standalone application that can be executed.
Example:
// Example of an Assembly with DLL csharp
public class MyClass {
public void PrintMessage() {
Console.WriteLine("Hello from MyClass");
}
}

If this code is compiled into MyAssembly.dll , it can be reused by other programs.

9. App Domain in .NET Framework


An App Domain provides isolation for running applications. It’s like a virtual machine within a process.
Each App Domain has its own security, configuration, and memory boundaries.
Example:
AppDomain domain1 = AppDomain.CreateDomain("Domain1"); csharp
AppDomain domain2 = AppDomain.CreateDomain("Domain2");

These app domains will not interfere with each other, and each will have its own set of application configurations.

10. Strong and Weak Assemblies in .NET Framework


Strongly Named Assemblies: Assemblies that have a unique identity, defined by a strong name (name, version,
culture, and public key).
Weakly Named Assemblies: Assemblies that do not have a unique identity and may cause conflicts.
Example (Strong Assembly):
sn -k mykey.snk bash

This command creates a strong name key ( mykey.snk ) that can be used to generate a strongly named assembly.

11. How to Install an Assembly into GAC in .NET Framework


The Global Assembly Cache (GAC) is a repository for assemblies that are shared across different applications. To
install an assembly into the GAC:
1. Use the Gacutil tool:
gacutil -i MyAssembly.dll bash

2. Or manually copy the assembly to the GAC folder:


C:\Windows\Assembly bash

Once in the GAC, assemblies can be used by any application on the system, regardless of the version.

12. DLL Hell Problem and Solution in .NET Framework


The DLL Hell problem occurs when different applications require different versions of the same DLL, leading to
version conflicts.
Solution:
Strongly Named Assemblies: Ensures that only a specific version of an assembly is used.
Side-by-Side Execution: Allows multiple versions of the same assembly to coexist, ensuring that each
application uses its required version.

Conclusion:
These .NET Framework concepts provide the foundational understanding required for building robust, scalable, and
secure applications in the .NET ecosystem. With features like CLR, CTS, CLS, Assembly Management, and App
Domain Isolation, the .NET Framework offers a comprehensive environment to manage applications across various
platforms and languages.
Here’s a detailed breakdown of the topics Reflection, Dynamic Type, Var Keyword, Volatile Keyword, and more in
C# with examples:

1. Reflection in C#
Reflection allows you to inspect the metadata of assemblies, modules, and types in C#. It enables you to access type
information at runtime, dynamically create objects, and invoke methods.
Key Features of Reflection:
Accessing Type Information: You can inspect the methods, properties, fields, and other metadata of types at
runtime.
Creating Objects Dynamically: You can create instances of objects at runtime.
Invoking Methods Dynamically: You can invoke methods and access properties dynamically.
Example: Using Reflection to get type information of a class
using System; csharp
using System.Reflection;

public class Person {


public string Name { get; set; }
public int Age { get; set; }

public void Introduce() {


Console.WriteLine($"Hi, my name is {Name} and I am {Age} years old.");
}
}

class Program {
static void Main() {
Person person = new Person() { Name = "John", Age = 30 };

Type type = person.GetType(); // Using Reflection to get type info

// Getting and displaying property info


PropertyInfo[] properties = type.GetProperties();
foreach (var property in properties) {
Console.WriteLine($"{property.Name}: {property.GetValue(person)}");
}

// Invoking method dynamically


MethodInfo method = type.GetMethod("Introduce");
method.Invoke(person, null); // Output: Hi, my name is John and I am 30 years
old.
}
}

Reflection allows you to inspect the properties ( Name , Age ) and invoke methods ( Introduce ) dynamically.

2. Dynamic Type in C#
The dynamic type in C# is used when you don't know the type of the object at compile-time, allowing for late binding.
Operations on dynamic objects are resolved at runtime.
Key Features of Dynamic:
Compile-time type checking is bypassed.
Runtime binding: Operations on dynamic objects are resolved at runtime.
Used in scenarios where reflection would be too complex or cumbersome (e.g., COM objects, interacting with
APIs like JavaScript, etc.).
Example:
dynamic dynamicObject = 5; // At compile time, dynamicObject is treated as an integer
csharp
Console.WriteLine(dynamicObject + 5); // Output: 10

dynamicObject = "Hello, World!"; // Now it's treated as a string


Console.WriteLine(dynamicObject + " from C#"); // Output: Hello, World! from C#

In this case, dynamicObject can change its type during runtime, and the operations are resolved accordingly.

3. Var Keyword in C#
The var keyword in C# allows implicit typing of variables. The type of the variable is determined by the compiler
based on the initializer expression at compile-time.
Key Features of var :
Compile-time type inference: The type of the variable is inferred based on the assigned value.
Must be initialized when declared.
Cannot be used without an initializer.
Example:
var number = 10; // Compiler infers the type as int csharp
var message = "Hello, C#"; // Compiler infers the type as string

Console.WriteLine(number); // Output: 10
Console.WriteLine(message); // Output: Hello, C#

The type of number is inferred as int , and the type of message is inferred as string .

4. Var vs Dynamic in C#
While both var and dynamic are used for flexible typing, there are important differences:
var : Used for compile-time type inference. The type is resolved at compile-time.

dynamic : Used for runtime type resolution. Type checking and binding happen at runtime.

Comparison Example:
var a = 10; // `a` is inferred as int at compile-time csharp
dynamic b = 10; // `b` is resolved at runtime

a = "Hello"; // Compile-time error: Cannot assign a string to an int


b = "Hello"; // Valid at runtime, no compile-time error

In the case of var , type safety is checked at compile time.


In the case of dynamic , type errors are caught only at runtime.

5. Dynamic vs Reflection in C#
dynamic : Used for late binding, but the type is resolved at runtime.
Reflection: A more powerful feature, allowing you to inspect types, methods, properties, and fields at runtime.
Comparison:
Dynamic is often simpler for basic cases like interacting with COM objects or when working with loosely typed
data.
Reflection gives you more control and allows you to inspect and manipulate types and metadata, but it is more
verbose.
Example:
// Using Dynamic csharp
dynamic obj = new Person();
obj.Name = "John"; // No compile-time checking
obj.Introduce(); // No compile-time checking
// Using Reflection
Type type = typeof(Person);
var person = Activator.CreateInstance(type);
PropertyInfo property = type.GetProperty("Name");
property.SetValue(person, "John");

MethodInfo method = type.GetMethod("Introduce");


method.Invoke(person, null); // Output: Hi, my name is John

Reflection gives you more flexibility, while dynamic simplifies runtime type resolution.

6. Volatile Keyword in C#
The volatile keyword in C# indicates that a field can be changed by different threads at any time, and prevents
the compiler from optimizing that field.
Key Features:
Ensures that reads and writes to the variable are directly done from and to memory.
Prevents compiler optimizations like caching the variable value.
Example:
class Program { csharp
private static volatile bool flag = false;

static void Main() {


Task.Run(() => {
Thread.Sleep(1000);
flag = true; // Thread 1 changes the value
});

while (!flag) {
// Main thread keeps checking flag without optimization
}

Console.WriteLine("Flag changed to true");


}
}

Without volatile , the compiler might cache the value of flag and the loop could run indefinitely. volatile
ensures that the latest value is always fetched from memory.

7. Ref vs Out in C#
Both ref and out are used to pass arguments by reference to methods. However, there are differences:
ref : The parameter must be initialized before being passed to the method.
out : The parameter doesn't need to be initialized before being passed to the method, but it must be assigned
a value before the method returns.
Example:
// Using ref csharp
void RefExample(ref int x) {
x = x + 1;
}

int a = 5;
RefExample(ref a);
Console.WriteLine(a); // Output: 6

// Using out
void OutExample(out int x) {
x = 10; // Must be initialized before returning
}

int b;
OutExample(out b);
Console.WriteLine(b); // Output: 10

ref requires the variable to be initialized before passing to the method.


out requires the method to initialize the variable before returning.

8. Named Parameters in C#
Named Parameters allow you to specify the argument by name rather than by position when calling a method. This
improves readability, especially when methods have multiple parameters.
Example:
class Program { csharp
static void DisplayInfo(string name, int age, string city) {
Console.WriteLine($"Name: {name}, Age: {age}, City: {city}");
}

static void Main() {


// Using named parameters
DisplayInfo(age: 25, name: "John", city: "New York");
}
}

In this example, we specify the arguments by name rather than by position. This can be particularly useful in methods
with many parameters.
Summary
Reflection: Allows runtime inspection and manipulation of types and metadata in an application.
Dynamic: Used for dynamic type resolution at runtime, without compile-time checks.
Var: A keyword for implicit typing where the type is inferred at compile time.
Volatile: Ensures the latest value of a variable is always fetched from memory, preventing optimizations.
Ref vs Out: Both pass parameters by reference, but ref requires initialization before use, while out requires
initialization inside the method.
Named Parameters: Enhances method calls by allowing arguments to be passed using the parameter names.
These concepts are crucial for developing flexible, maintainable, and efficient applications in C#, especially when
dealing with dynamic scenarios, multithreading, or reflection-based operations.

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy