Coding Interview QuestionsInterview Questions and Answers

All New 90 C# Interview Questions

Table of Contents

Introduction

Are you looking for an introduction to an article containing 90 C# interview questions and answers? I can help you with that.

Preparing for a C# interview can be a daunting task, especially when you’re unsure of the questions that might be thrown your way. In this comprehensive article, we have compiled a list of 90 carefully selected C# interview questions and their corresponding answers. Whether you’re a beginner looking to land your first C# job or an experienced developer aiming to brush up on your knowledge, these questions will serve as an excellent resource to help you succeed in your interview. So, let’s dive in and explore the world of C# programming!

Basic Questions

1. What is C#?

C# is a general-purpose, object-oriented programming language developed by Microsoft. It was designed to be simple, modern, and versatile, primarily used for building software applications on the Microsoft .NET framework. C# combines elements of C and C++ with additional features for easier development and enhanced productivity.

2. Who developed C# and why?

C# was developed by Microsoft Corporation in the early 2000s. The language was created by a team led by Anders Hejlsberg, with the aim of providing a modern programming language for developing Windows applications and web services on the .NET platform. C# was designed to be easy to use, robust, and highly interoperable with other languages in the .NET ecosystem.

3. How is C# different from C++ and Java?

FeatureC#C++Java
Memory ManagementGarbage collectedManual memory managementGarbage collected
Platform IndependenceRuns on .NET frameworkCompiles to machine codeRuns on Java Virtual Machine (JVM)
Multiple InheritanceNoYesNo
Operator OverloadingYesYesNo
Checked ExceptionsNoYesYes
PointersLimited useFull supportNo
NamespacesYesYesYes
DelegatesYesYesNo
Libraries.NET FrameworkStandard Template Library (STL), BoostJava Standard Library
Primary UseWindows development, web servicesSystem-level programming, game developmentEnterprise applications, Android development

4. What are the basic data types used in C#?

In C#, the basic data types include:

  • bool: Represents a Boolean value (true or false).
  • byte: Represents an 8-bit unsigned integer.
  • sbyte: Represents an 8-bit signed integer.
  • short: Represents a 16-bit signed integer.
  • ushort: Represents a 16-bit unsigned integer.
  • int: Represents a 32-bit signed integer.
  • uint: Represents a 32-bit unsigned integer.
  • long: Represents a 64-bit signed integer.
  • ulong: Represents a 64-bit unsigned integer.
  • float: Represents a 32-bit floating-point number.
  • double: Represents a 64-bit floating-point number.
  • decimal: Represents a decimal number with higher precision.
  • char: Represents a single Unicode character.
  • string: Represents a sequence of characters.
  • object: Represents a base type from which all types are derived.

5. Can you explain what a Namespace is in C#?

In C#, a namespace is a way to organize and group related code elements such as classes, structs, interfaces, and other namespaces. It helps avoid naming conflicts and provides a hierarchical organization of code.

A namespace provides a logical separation of code and allows you to define multiple classes with the same name as long as they are in different namespaces. It also helps in organizing and maintaining large codebases by providing a clear structure.

Here’s an example of using a namespace in C#:

C#
namespace MyNamespace
{
    class MyClass
    {
        // Class members and methods
    }
}

In the example above, MyClass is defined within the MyNamespace namespace. To use the class, you would typically reference the namespace using the using directive or fully qualify the class name with the namespace when using it in another code file.

6. What are Value Types and Reference Types in C#?

In C#, variables can be categorized into two types: value types and reference types.

  • Value Types: Value types store their values directly, and each variable has its own copy of the data. Examples of value types in C# include numeric types (int, float, etc.), Boolean (bool), and structures (struct). When you assign a value type variable to another, a copy of the value is made.

Example:

C#
int x = 5;
int y = x; // y gets a copy of the value stored in x
x = 10;   // Changing x doesn't affect the value of y
  • Reference Types: Reference types store a reference to the data, and multiple variables can refer to the same underlying object. Examples of reference types in C# include classes (class), arrays, and strings. When you assign a reference type variable to another, both variables refer to the same object in memory.

Example:

C#
MyClass obj1 = new MyClass();
MyClass obj2 = obj1; // Both obj1 and obj2 refer to the same object
obj1.SomeProperty = 10;
Console.WriteLine(obj2.SomeProperty); // Prints 10, changes are visible through both variables

7. What do you understand by Boxing and Unboxing in C#?

  • Boxing: Boxing is the process of converting a value type to a reference type by wrapping it in an object. When you box a value type, a new object is created on the heap, and the value is stored within it. Boxing allows value types to be treated as objects and used in scenarios that require reference types.

Example:

C#
int x = 5;
object boxed = x; // Boxing the value of x
  • Unboxing: Unboxing is the reverse process of converting a reference type (previously boxed value type) back to its original value type. Unboxing extracts the value from the boxed object and assigns it to a value type variable.

Example:

C#
object boxed = 5; // Boxed integer value
int x = (int)boxed; // Unboxing the value and assigning it to x

It’s important to note that boxing and unboxing operations come with performance overhead, as they involve memory allocations and type checks. Therefore, it’s generally recommended to minimize their usage for better performance.

8. How would you explain the concept of Class and Object in C#?

  • Class: In C#, a class is a blueprint or a template that defines the structure and behavior of objects. It serves as a blueprint for creating instances of objects that share the same characteristics and behaviors. A class defines the properties, methods, and events that an object of that class can have.

Example:

C#
class Person
{
    // Class members: fields, properties, methods, etc.
    public string Name { get; set; }
    public void SayHello()
    {
        Console.WriteLine($"Hello, my name is {Name}.");
    }
}
  • Object: An object is an instance of a class. It represents a concrete entity based on the class definition. Objects have state (properties) and behavior (methods) defined by the class they belong to. Multiple objects can be created from the same class, each having its own state and interacting independently with the program.
C#
Person person1 = new Person();
person1.Name = "John";
person1.SayHello(); // Output: Hello, my name is John.

In the example above, Person is a class that defines the blueprint for creating person objects. person1 is an object created from the Person class, and it has its own Name property and SayHello() method. By calling the SayHello() method on the person1 object, it outputs a personalized greeting based on the object’s Name property.

Certainly! I’ll provide concise answers to the remaining questions.

9. What is a Method in C#? Can you provide an example?

A method in C# is a block of code that performs a specific task. It can be called or invoked to execute the code within it. Methods are used to organize and modularize code, making it reusable and easier to maintain. Here’s an example:

C#
public void Greet(string name)
{
    Console.WriteLine($"Hello, {name}!");
}

In the example above, Greet is a method that takes a string parameter name and prints a greeting message with the provided name.

11. What is an Array in C#?

An array in C# is a data structure that stores a fixed-size sequence of elements of the same type. Arrays allow efficient storage and retrieval of multiple values under a single variable name. Elements within an array can be accessed using an index starting from 0. Here’s an example:

C#
int[] numbers = new int[5]; // Declaration and initialization of an integer array
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
numbers[3] = 4;
numbers[4] = 5;

Console.WriteLine(numbers[2]); // Output: 3

13. What is the difference between an Array and a List in C#?

FeatureArrayList
SizeFixed (determined at creation)Dynamic (can grow or shrink)
InitializationLength must be specified at creationLength is not required at creation
ElementsAccessed by indexAccessed by index or iteration
MemoryContiguous memory allocationContiguous or fragmented memory allocation
MethodsLimited built-in methodsExtensive built-in methods
FlexibilityLess flexibleMore flexible

14. How can you define a Constant in C#?

In C#, you can define a constant using the const keyword. Constants are variables whose values cannot be changed once assigned, and they must be initialized at the time of declaration. Here’s an example:

C#
const int MaxValue = 100;

In the example above, MaxValue is a constant of type int with a value of 100. It cannot be modified throughout the program.

15. What is the use of the ‘using’ statement in C#?

The using statement in C# is used for automatic disposal of resources. It ensures that objects implementing the IDisposable interface are properly cleaned up and released when they are no longer needed. The using statement defines a scope within which the resource is available and automatically disposes of it at the end of the scope. Here’s an example:

C#
using (var stream = new FileStream("data.txt", FileMode.Open))
{
    // Read or write operations with the stream
}

In the example above, the FileStream object is created and used within the using statement. Once the code execution exits the scope, the Dispose() method of the FileStream object is automatically called, releasing any acquired resources.

16. What do you mean by Static and Dynamic types in C#?

  • Static types: Static typing in C# means that variables are bound to a specific type at compile-time and their types cannot change during runtime. Static typing provides compile-time type checking and helps detect type-related errors early in the development process.

Example:

C#
int age = 25; // Static typing, age is of type int
  • Dynamic types: Dynamic typing allows variables to hold values of different types and enables late binding, where type checking is done during runtime. The type of a dynamic variable can change during execution.

Example:

C#
dynamic value = 10; // Dynamic typing, value is initially of type int
value = "Hello";   // Type of value changes to string

17. What is Exception Handling in C#?

Exception handling in C# is a mechanism to handle and recover from runtime errors or exceptional conditions that may occur during program execution. Exceptions are objects that represent errors or abnormal situations. With exception handling, you can catch and handle exceptions, preventing your program from terminating abruptly. It provides a structured way to handle and recover from errors, ensuring the program’s stability.

18. Can you explain what a Try, Catch, Finally block is?

  • try: The try block is used to enclose the code that may throw an exception. It is followed by one or more catch blocks or a finally block.
  • catch: The catch block is used to catch and handle specific exceptions that may occur within the try block. Multiple catch blocks can be used to handle different types of exceptions. If an exception is thrown, the corresponding catch block with a matching exception type is executed.
  • finally: The finally block is optional and follows the try and catch blocks. It is used to define code that should always be executed, regardless of whether an exception occurred or not. The finally block is typically used to release resources or perform cleanup operations.

Example:

C#
try
{
    // Code that may throw an exception
}
catch (DivideByZeroException ex)
{
    // Exception handling specific to DivideByZeroException
}
catch (Exception ex)
{
    // Exception handling for any other exception type
}
finally
{
    // Cleanup code or resource release
}

19. What is a Null Reference Exception?

A Null Reference Exception occurs when a null reference is encountered where an object reference is expected. It indicates an attempt to access a member (property, method, etc.) of an object that is null. This exception is raised at runtime and can be caught and handled using exception handling mechanisms.

Example:

C#
string name = null;
int length = name.Length; // NullReferenceException: Object reference not set to an instance of an object

In the example above, the Length property of the name string is accessed, but since name is null, a Null Reference Exception is thrown.

20. Can you explain the concept of Inheritance in C#?

Inheritance is a fundamental concept in object-oriented programming that allows a class to inherit the characteristics (fields, properties, and methods) of another class. The class being inherited from is called the base class or parent class, and the class inheriting is called the derived class or child class. Inheritance enables code reuse, extensibility, and the creation of hierarchical relationships between classes.

Example:

C#
class Animal
{
    public void Eat()
    {
        Console.WriteLine("Eating...");
    }
}

class Dog : Animal
{
    public void Bark()
    {
        Console.WriteLine("Woof!");
    }
}

In the example above, the Dog class inherits from the Animal class. The Dog class has access to the Eat() method from the Animal class, and it adds its own Bark() method.

21. What do you understand by Polymorphism in C#?

Polymorphism is a concept in object-oriented programming that allows objects of different types to be treated as instances of a common base type. It enables the use of a single interface or base class to represent various concrete implementations. Polymorphism provides flexibility, code reusability, and the ability to create code that is more generic and extensible.

There are two forms of polymorphism in C#:

  • Compile-time Polymorphism: Also known as method overloading, it allows different methods with the same name but different parameter lists to be defined in a class. The appropriate method is selected based on the arguments passed during compile-time.

Example:

C#
class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public double Add(double a, double b)
    {
        return a + b;
    }
}
  • Runtime Polymorphism: Also known as method overriding, it allows a derived class to provide its own implementation of a method that is already defined in its base class. The appropriate method is selected based on the actual type of the object during runtime.

Example:

C#
class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a shape");
    }
}

class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

In the example above, the Draw() method is overridden in the Circle class, providing a different implementation. When calling Draw() on a Circle object, the overridden method in the Circle class will be executed.

22. What are Properties in C#?

Properties in C# provide a way to encapsulate fields within a class and control their access. They are an extension of fields and allow you to define custom logic for getting and setting their values. Properties provide a clean interface for interacting with the internal state of an object and enable data abstraction and encapsulation.

Example:

C#
class Person
{
    private string name;

    public string Name
    {
        get { return name; }
        set { name = value; }
    }
}

In the example above, Name is a property that encapsulates the name field. The get accessor retrieves the value of name, and the set accessor assigns a value to it. This allows controlled access to the name field.

23. What is the difference between a Struct and a Class in C#?

FeatureStructClass
TypeValue typeReference type
MemoryStored on stackStored on heap
Default valuesZero-initializedNull
InheritanceDoes not supportSupports single inheritance
ConstructorSupports parameterless constructor and custom constructorsSupports parameterless constructor and custom constructors
Boxing/UnboxingValue type, requires explicit boxing and unboxingReference type, no boxing/unboxing needed
UsageSuitable for small, lightweight data structuresSuitable for complex objects and behaviors

24. How would you differentiate between Continue and Break statement in C#?

StatementDescription
continueUsed inside loops to skip the remaining code within the loop body for the current iteration and proceed to the next iteration.
breakUsed inside loops and switch statements to terminate the loop or switch block and exit immediately. The control flow moves to the next statement outside the loop or switch.

25. Can you explain the ‘this’ keyword in C#?

The this keyword in C# is a reference to the current instance of a class or struct. It is used to access members (fields, properties, and methods) of the current object within the class or struct. It helps differentiate between instance variables and method parameters or local variables with the same name.

Example:

C#
class Person
{
    private string name;

    public Person(string name)
    {
        this.name = name; // 'this' refers to the current instance of the Person class
    }
}

In the example above, the this keyword is used to assign the name parameter value to the name field of the current Person object.

26. What is the use of the ‘base’ keyword in C#?

The base keyword in C# is used to access members (fields, properties, and methods) of the base class from within a derived class. It is particularly useful when a derived class overrides a member of the base class but still wants to access the original implementation.

Example:

C#
class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a shape");
    }
}

class Circle : Shape
{
    public override void Draw()
    {
        base.Draw(); // Calls the Draw() method of the base class
        Console.WriteLine("Drawing a circle");
    }
}

In the example above, the base.Draw() statement is used in the Circle class to call the Draw() method of the base Shape class before providing its own implementation.

27. How do you declare an Interface in C#?

In C#, you can declare an interface using the interface keyword. An interface defines a contract that specifies a set of members (properties, methods, events) that a class implementing the interface must implement. Interfaces provide a way to achieve multiple inheritances of behavior in C#.

Example:

C#
interface IShape
{
    void Draw();
    double CalculateArea();
}

In the example above, IShape is an interface that declares two members: Draw() and CalculateArea(). Any class that implements the IShape interface must provide implementations for these members.

28. What is Method Overloading in C#?

Method overloading in C# allows multiple methods in a class to have the same name but different parameter lists. It provides a way to define methods with similar functionality but with different input parameters. The appropriate method is automatically selected based on the arguments passed during the method invocation.

Example:

C#
class Calculator
{
    public int Add(int a, int b)
    {
        return a + b;
    }

    public double Add(double a, double b)
    {
        return a + b;
    }
}

In the example above, the Add method is overloaded with two versions: one for integers and one for doubles. The appropriate Add method will be called based on the argument types used when invoking the method.

29. Can you explain what Constructors and Destructors are in C#?

  • Constructors: Constructors are special methods in a class that are used to initialize objects of that class. They have the same name as the class and do not have a return type. Constructors are called automatically when an object is created. They allow you to set initial values to the fields or perform any necessary setup operations.

Example:

C#
class Person
{
    public string Name { get; set; }

    public Person(string name)
    {
        Name = name;
    }
}

In the example above, the Person class has a constructor that takes a name parameter and assigns it to the Name property of the object being created.

  • Destructors: Destructors, also known as finalizers, are special methods in a class that are called automatically just before an object is destroyed by the garbage collector. Destructors are used to release unmanaged resources, such as file handles or network connections, held by the object. In C#, destructors are implemented using the tilde (~) character followed by the class name.

Example:

C#
class MyClass
{
    ~MyClass()
    {
        // Cleanup code for releasing unmanaged resources
    }
}

In the example above, the destructor of the MyClass class is defined to perform cleanup operations before the object is destroyed.

30. What are Delegates in C#?

Delegates in C# are objects that hold references to methods, allowing method invocation through the delegate. They provide a way to encapsulate and pass methods as parameters, enabling callback mechanisms and event handling. Delegates are mainly used for implementing events and callback functions in C#.

Example:

C#
delegate void PrintDelegate(string message);

class Printer
{
    public void PrintMessage(string message)
    {
        Console.WriteLine(message);
    }
}

class Program
{
    static void Main()
    {
        Printer printer = new Printer();
        PrintDelegate printDelegate = printer.PrintMessage;

        printDelegate("Hello, delegates!"); // Invokes the PrintMessage method through the delegate
    }
}

In the example above, a delegate named PrintDelegate is defined to hold a reference to the PrintMessage method of the Printer class. The delegate is then used to invoke the method.

31. What is the use of the ‘new’ keyword in C#?

The new keyword in C# is used to create objects and instances of a class. It is used in two different contexts:

  • Object Creation: The new keyword is used to create a new instance of a class. It invokes the constructor of the class and allocates memory for the object.

Example:

C#
MyClass myObject = new MyClass();

In the example above, the new keyword is used to create a new instance of the MyClass class and assign it to the myObject variable.

  • Method Hiding: The new keyword is used to hide a method from a base class when a derived class declares a method with the same name. This is called method hiding.

Example:

C#
class BaseClass
{
    public void MyMethod()
    {
        Console.WriteLine("Base class method");
    }
}

class DerivedClass : BaseClass
{
    public new void MyMethod()
    {
        Console.WriteLine("Derived class method");
    }
}

In the example above, the new keyword is used to hide the MyMethod of the base class in the derived class. The method in the derived class is invoked when called using an instance of the derived class.

32. Can you explain the concept of Multithreading in C#?

Multithreading in C# is the concurrent execution of multiple threads within a single application. A thread is a lightweight unit of execution that can run independently and perform a specific task. Multithreading allows different parts of a program to run concurrently, achieving parallelism and improved performance.

C# provides several classes and mechanisms for working with multithreading, such as the Thread class, Task class, and async/await keywords. Multithreading is used to perform tasks simultaneously, handle user interface responsiveness, improve resource utilization, and execute time-consuming operations without blocking the main thread.

Example:

C#
using System;
using System.Threading;

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

        // Continue with other tasks while DoWork runs in the background

        thread.Join(); // Wait for the thread to complete
        Console.WriteLine("Work completed.");
    }

    static void DoWork()
    {
        // Perform some time-consuming work here
        Thread.Sleep(3000);
        Console.WriteLine("Work done!");
    }
}

In the example above, a new thread is created using the Thread class to execute the DoWork method concurrently with the main thread. The main thread continues its execution while the DoWork method runs in the background. The Join() method is used to wait for the background thread to complete before printing the completion message.

33. What is LINQ in C#?

LINQ (Language-Integrated Query) is a set of language extensions in C# that provide a uniform way to query and manipulate data from different data sources such as arrays, collections, databases, XML, and more. It allows developers to write declarative queries against data sources using a common syntax and provides powerful operations for filtering, sorting, grouping, and transforming data.

LINQ provides a seamless integration of query capabilities directly into C# code, improving readability, maintainability, and reducing the amount of code needed to perform complex data operations.

Example:

C#
int[] numbers = { 1, 2, 3, 4, 5 };

var evenNumbers = from num in numbers
                  where num % 2 == 0
                  select num;

foreach (var num in evenNumbers)
{
    Console.WriteLine(num);
}

In the example above, LINQ is used to query the numbers array and retrieve the even numbers. The query expression uses the from, where, and select keywords to filter and select the desired elements. The result is then iterated using a foreach loop.

34. Can you explain the concept of Lambda Expressions in C#?

Lambda expressions in C# are anonymous functions that allow you to create delegates or expression trees in a more concise and readable way. They provide a simplified syntax for defining inline functions without explicitly declaring a separate method.

Lambda expressions are particularly useful when working with LINQ queries, functional programming, and event handling.

Example:

C#
Func<int, int, int> add = (x, y) => x + y;
int result = add(3, 5); // Result will be 8

In the example above, a lambda expression (x, y) => x + y defines an anonymous function that takes two integers x and y and returns their sum. The lambda expression is assigned to a Func<int, int, int> delegate named add, which can then be invoked like a regular method.

35. What is Garbage Collection in C#?

Garbage Collection in C# is an automatic memory management mechanism that frees up memory occupied by objects that are no longer in use. It automatically identifies and deallocates memory that is no longer reachable by the program, reducing memory leaks and manual memory management errors.

The Garbage Collector (GC) tracks objects’ usage and determines when they are eligible for garbage collection. It scans the managed heap, identifies objects that are no longer referenced, and reclaims their memory. The GC runs in the background, periodically reclaiming memory and ensuring efficient memory utilization.

C# provides a managed memory model, where developers don’t have to explicitly free memory. Instead, they rely on the Garbage Collector to handle memory deallocation.

36. How does Asynchronous Programming work in C#?

Asynchronous programming in C# allows executing tasks concurrently, without blocking the execution of the main thread. It is particularly useful when performing I/O operations or CPU-intensive tasks that may cause the application to become unresponsive.

C# provides the async and await keywords to simplify asynchronous programming. The async keyword is used to declare an asynchronous method, and the await keyword is used to await the completion of an asynchronous operation without blocking the execution.

When an await statement is encountered, the method is suspended, and control is returned to the caller. The execution continues with the remaining code outside the await block. Once the awaited operation completes, the method resumes execution from where it left off.

Asynchronous programming in C# relies on the use of tasks and the Task-based Asynchronous Pattern (TAP). It allows developers to write non-blocking, responsive code that improves performance and scalability.

Example:

C#
async Task<string> DownloadDataAsync()
{
    HttpClient client = new HttpClient();
    string data = await client.GetStringAsync("https://example.com/api/data");
    return data;
}

async Task Main()
{
    string result = await DownloadDataAsync();
    Console.WriteLine(result);
}

In the example above, the DownloadDataAsync method uses the await keyword to asynchronously download data from a web API. The Main method awaits the completion of the asynchronous operation and prints the downloaded data. Asynchronous programming allows the program to perform other tasks while waiting for the data to be downloaded, improving responsiveness.

Intermediate Questions

1. What is an Abstract Class in C#?

An abstract class in C# is a class that cannot be instantiated directly and serves as a blueprint for deriving other classes. It is marked with the abstract keyword. An abstract class can have both abstract and non-abstract members. It is designed to be inherited by other classes, which must provide implementations for the abstract members defined in the abstract class.

Example:

C#
abstract class Shape
{
    public abstract double CalculateArea(); // Abstract method

    public void Display()
    {
        Console.WriteLine("This is a shape.");
    }
}

class Rectangle : Shape
{
    private double length;
    private double width;

    public Rectangle(double length, double width)
    {
        this.length = length;
        this.width = width;
    }

    public override double CalculateArea()
    {
        return length * width;
    }
}

class Program
{
    static void Main()
    {
        Rectangle rectangle = new Rectangle(5, 3);
        Console.WriteLine("Area of rectangle: " + rectangle.CalculateArea());
    }
}

In this example, the Shape class is defined as an abstract class. It contains an abstract method CalculateArea() and a non-abstract method Display(). The Rectangle class inherits from the Shape class and provides an implementation for the CalculateArea() method.

2. What are Sealed Classes in C#?

A sealed class in C# is a class that cannot be inherited or used as a base class for other classes. It is marked with the sealed keyword. By sealing a class, you prevent other classes from inheriting from it and extending its functionality.

Example:

C#
sealed class MySealedClass
{
    // Class members and methods
}

class DerivedClass : MySealedClass  // This will result in a compilation error
{
    // Derived class implementation
}

In this example, the MySealedClass is marked as sealed, so it cannot be inherited by any other class. If you try to derive a class from MySealedClass, such as the DerivedClass, a compilation error will occur.

3. Explain Delegates in C# with an example.

In C#, a delegate is a type that represents a method signature, allowing you to pass methods as parameters or store them in variables. It provides a way to achieve function pointers or callbacks. Delegates are widely used in event handling and asynchronous programming.

Example:

C#
delegate void MyDelegate(string message);

class Program
{
    static void Main()
    {
        MyDelegate delegate1 = new MyDelegate(Method1);
        MyDelegate delegate2 = new MyDelegate(Method2);

        delegate1("Hello");
        delegate2("World");
    }

    static void Method1(string message)
    {
        Console.WriteLine("Method1: " + message);
    }

    static void Method2(string message)
    {
        Console.WriteLine("Method2: " + message);
    }
}

In this example, we define a delegate MyDelegate that represents a method taking a string parameter and returning void. We create two instances of MyDelegate by assigning them different methods. We can then invoke these delegate instances, passing a string parameter to each method.

The output will be:

C#
Method1: Hello
Method2: World

4. What is the difference between out and ref parameters?

Propertyref Parametersout Parameters
InitializationMust be initialized before passing to the method.Not required to be initialized before passing to the method.
Method BehaviorBoth read and write.Only write (the method must assign a value to the parameter).
Usage Inside the MethodCan be used to pass values in and out of the method.Primarily used to return multiple values from a method.
Caller’s ResponsibilityMust initialize the parameter before passing and it must have a value.Not required to initialize the parameter before passing, but the caller must assign a value after the method call.

In summary, ref parameters require initialization before passing, support bidirectional communication, and the caller must ensure the parameter is already assigned. out parameters do not require initialization before passing, are primarily used for returning multiple values, and the caller must assign a value to the parameter after the method call.

5. What is the virtual keyword in C#?

The virtual keyword in C# is used to declare a method, property, or indexer that can be overridden in derived classes. It allows classes to provide a default implementation of a member that can be modified or extended by derived classes. The virtual keyword is used in the base class, and the override keyword is used in the derived class to indicate the overridden implementation.

Example:

C#
class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a shape");
    }
}

class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

class Program
{
    static void Main()
    {
        Shape shape = new Shape();
        shape.Draw();  // Output: Drawing a shape

        Circle circle = new Circle();
        circle.Draw();  // Output: Drawing a circle

        Shape shape2 = new Circle();
        shape2.Draw();  // Output: Drawing a circle (Polymorphism)
    }
}

In this example, the Shape class has a virtual method Draw(). The Circle class inherits from Shape and overrides the Draw() method. When we create instances of Shape and Circle, calling the Draw() method will invoke the appropriate implementation based on the actual type of the object. The last example demonstrates polymorphism, where a Circle object is assigned to a Shape reference variable, and calling Draw() will execute the overridden method in Circle.

6. Can you explain Encapsulation in C#?

Encapsulation in C# is a mechanism that bundles data and the methods that operate on that data into a single unit called a class. It provides a way to hide the internal details and implementation of an object from the outside world. Encapsulation helps achieve data abstraction, data protection, and code organization.

By using access modifiers such as private, protected, public, and internal, you can control the visibility and accessibility of members within a class. Typically, data members (fields or properties) are marked as private and accessed through public methods (getters and setters) to enforce encapsulation.

Example:

C#
class Car
{
    private string brand;
    private string model;
    private int year;

    public string Brand
    {
        get { return brand; }
        set { brand = value; }
    }

    public string Model
    {
        get { return model; }
        set { model = value; }
    }

    public int Year
    {
        get { return year; }
        set { year = value; }
    }

    public void StartEngine()
    {
        Console.WriteLine("Engine started");
    }
}

class Program
{
    static void Main()
    {
        Car car = new Car();
        car.Brand = "Toyota";
        car.Model = "Camry";
        car.Year = 2022;

        Console.WriteLine("Car: " + car.Brand + " " + car.Model + " " + car.Year);
        car.StartEngine();
    }
}

In this example, the Car class encapsulates the data members brand, model, and year as private fields. Public properties (Brand, Model, and Year) provide controlled access to these private fields. The StartEngine() method is also encapsulated within the class and can be accessed externally.

7. What is an Automatic Property in C#?

An automatic property in C# is a shorthand syntax for declaring properties that automatically generate the underlying private field. It simplifies the process of creating properties without explicitly defining the backing field. The compiler automatically generates the field and the standard get/set accessors.

Example:

C#
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

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

        Console.WriteLine("Name: " + person.Name);
        Console.WriteLine("Age: " + person.Age);
    }
}

In this example, the Person class has two automatic properties Name and Age. These properties do not have explicit private fields defined. The compiler automatically generates the fields and the default get/set accessors for these properties. We can access and assign values to these properties as if they were regular fields.

8. What is an Indexer in C#?

An indexer in C# is a special type of property that allows objects to be indexed like arrays. It enables instances of a class or struct to be accessed using the indexing syntax ([]). By implementing an indexer, you can define custom behavior for getting and setting values within an object.

Example:

C#
class MyCollection
{
    private int[] data = new int[5];

    public int this[int index]
    {
        get { return data[index]; }
        set { data[index] = value; }
    }
}

class Program
{
    static void Main()
    {
        MyCollection collection = new MyCollection();
        collection[0] = 1;
        collection[1] = 2;
        collection[2] = 3;

        Console.WriteLine(collection[0]);  // Output: 1
        Console.WriteLine(collection[1]);  // Output: 2
        Console.WriteLine(collection[2]);  // Output: 3
    }
}

In this example, the MyCollection class defines an indexer this[int index], which allows accessing and setting elements in the data array using the indexing syntax. We can assign values to collection[0], collection[1], and collection[2] and retrieve their values using the same syntax.

9. Explain Late Binding in C#.

Late binding in C# is a mechanism that allows resolving method calls or accessing members at runtime rather than at compile time. It provides the flexibility to determine the method or member to invoke based on runtime conditions. Late binding is achieved using features like reflection or dynamic types.

Example using Reflection:

C#
using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        Type type = Type.GetType("MyNamespace.MyClass");
        object instance = Activator.CreateInstance(type);

        MethodInfo method = type.GetMethod("MyMethod");
        method.Invoke(instance, null);
    }
}

namespace MyNamespace
{
    class MyClass
    {
        public void MyMethod()
        {
            Console.WriteLine("Late-bound method call");
        }
    }
}

In this example, late binding is accomplished using reflection. The code dynamically loads the type MyClass at runtime and creates an instance of it. Then, using reflection, it obtains the MyMethod method and invokes it.

10. What is Operator Overloading in C#?

Operator overloading in C# allows you to redefine the behavior of operators (+, -, *, /, etc.) for your custom types. It enables you to use operators on objects in a way that makes sense for the specific type. Operator overloading provides a more natural and intuitive syntax for working with objects.

Example:

C#
class ComplexNumber
{
    public int Real { get; set; }
    public int Imaginary { get; set; }

    public ComplexNumber(int real, int imaginary)
    {
        Real = real;
        Imaginary = imaginary;
    }

    public static ComplexNumber operator +(ComplexNumber c1, ComplexNumber c2)
    {
        int real = c1.Real + c2.Real;
        int imaginary = c1.Imaginary + c2.Imaginary;
        return new ComplexNumber(real, imaginary);
    }
}

class Program
{
    static void Main()
    {
        ComplexNumber c1 = new ComplexNumber(2, 3);
        ComplexNumber c2 = new ComplexNumber(4, 5);

        ComplexNumber sum = c1 + c2;
        Console.WriteLine("Sum: " + sum.Real + " + " + sum.Imaginary + "i");
    }
}

In this example, we define a ComplexNumber class to represent complex numbers. We overload the + operator to add two complex numbers together. By defining the + operator as a static method in the ComplexNumber class, we can use the + operator to add two instances of ComplexNumber together.

11. What are Extension Methods in C#?

Extension methods in C# allow you to add new methods to existing types without modifying their original definitions or creating derived classes. They provide a way to extend the functionality of types from other libraries, including built-in types or third-party types.

Example:

C#
using System;

static class StringExtensions
{
    public static string Reverse(this string input)
    {
        char[] chars = input.ToCharArray();
        Array.Reverse(chars);
        return new string(chars);
    }
}

class Program
{
    static void Main()
    {
        string str = "Hello, World!";
        string reversed = str.Reverse();

        Console.WriteLine("Original: " + str);
        Console.WriteLine("Reversed: " + reversed);
    }
}

In this example, we define an extension method Reverse() for the string type. The this keyword before the first parameter indicates that it is an extension method for the string type. The extension method Reverse() reverses the characters in a string. We can then call this method on any string instance, as if it were a member method of the string type.

12. What is LINQ and why is it useful?

LINQ (Language-Integrated Query) is a set of language features and APIs in C# that provide a consistent way to query and manipulate data from different data sources, such as collections, arrays, databases, XML, and more. It allows you to express queries in a declarative manner, making it easier to work with data.

LINQ provides several benefits:

  • Simplicity: LINQ offers a unified syntax for querying different data sources, reducing the learning curve and code complexity.
  • Type Safety: LINQ expressions are checked at compile-time, catching errors early.
  • Readability: LINQ queries use a familiar SQL-like syntax, making code more readable and understandable.
  • Code Reusability: LINQ queries can be easily reused across different data sources without modifying the query logic.
  • Optimization: LINQ providers can optimize queries for better performance based on the underlying data source.

Example:

C#
using System;
using System.Linq;

class Program
{
    static void Main()
    {
        int[] numbers = { 1, 2, 3, 4, 5 };

        var evenNumbers = numbers.Where(n => n % 2 == 0);

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

In this example, we use LINQ to query the numbers array and filter out the even numbers. The Where() method takes a lambda expression as a predicate to specify the condition for filtering. The result is stored in the evenNumbers variable and then displayed using a foreach loop. LINQ simplifies the process of filtering, transforming, and querying data in a concise and readable manner.

13. What are Partial Classes in C#?

Partial classes in C# allow the definition of a class to be split into multiple files within the same assembly. Each part of the class is declared with the partial keyword. The compiler merges all the partial class files into a single class definition at compile time. This feature is useful when multiple developers are working on different parts of a class or when generated code needs to be combined with manually written code.

Example:
File 1: MyClass_Part1.cs

C#
partial class MyClass
{
    public void Method1()
    {
        // Implementation for Method1
    }
}

File 2: MyClass_Part2.cs

C#
partial class MyClass
{
    public void Method2()
    {
        // Implementation for Method2
    }
}

Main program:

C#
class Program
{
    static void Main()
    {
        MyClass myClass = new MyClass();
        myClass.Method1();
        myClass.Method2();
    }
}

In this example, the MyClass is declared as a partial class. The class definition is split into two files, MyClass_Part1.cs and MyClass_Part2.cs. Each file contains a partial definition of the class with different methods. When the program is compiled, the compiler merges the partial class definitions into a single class. The Main() method can create an instance of MyClass and call both Method1() and Method2().

14. Explain Tuples in C#.

Tuples in C# are lightweight data structures that allow you to combine multiple values of different types into a single object. They provide a way to group related values without the need to define a custom class or structure explicitly. Tuples are especially useful when you need to return multiple values from a method or store multiple values in a collection.

Example:

C#
var person = ("John", 30, "USA");

Console.WriteLine("Name: " + person.Item1);
Console.WriteLine("Age: " + person.Item2);
Console.WriteLine("Country: " + person.Item3);

In this example, we create a tuple person that combines a name, age, and country. The values are enclosed in parentheses and separated by commas. We can access the individual elements of the tuple using the Item1, Item2, Item3, etc., properties.

Starting from C# 7.0, named tuples were introduced, allowing you to provide names for the elements of the tuple. This improves the readability and makes the code more self-explanatory.

Example with named tuple:

C#
var person = (Name: "John", Age: 30, Country: "USA");

Console.WriteLine("Name: " + person.Name);
Console.WriteLine("Age: " + person.Age);
Console.WriteLine("Country: " + person.Country);

In this example, the elements of the tuple are given names (Name, Age, Country), making it easier to access them using the named properties.

15. Can you explain what an Anonymous Method is in C#?

An anonymous method in C# is a method without a name. It allows you to define and use a method in place without explicitly declaring a separate method. Anonymous methods are primarily used as event handlers or as parameters for methods that accept delegates.

Example:

C#
delegate void MyDelegate(string message);

class Program
{
    static void Main()
    {
        MyDelegate delegate1 = delegate (string message)
        {
            Console.WriteLine("Anonymous method: " + message);
        };

        delegate1("Hello");
    }
}

In this example, an anonymous method is assigned to a delegate delegate1. The anonymous method takes a string parameter and writes a message to the console. The anonymous method is invoked using the delegate, passing the argument "Hello". The output will be Anonymous method: Hello.

16. What is a Jagged Array in C#?

A jagged array in C# is an array whose elements are arrays. It is also known as an array-of-arrays. Unlike a multidimensional array, a jagged array allows each element to have a different length, providing flexibility when working with irregularly shaped data.

Example:

C#
int[][] jaggedArray = new int[3][];
jaggedArray[0] = new int[] { 1, 2, 3 };
jaggedArray[1] = new int[] { 4, 5 };
jaggedArray[2] = new int[] { 6 };

Console.WriteLine(jaggedArray[0][0]);  // Output: 1
Console.WriteLine(jaggedArray[1][1]);  // Output: 5
Console.WriteLine(jaggedArray[2][0]);  // Output: 6

In this example, we create a jagged array jaggedArray with three elements. Each element is an array of integers. The lengths of the inner arrays can vary. We assign different arrays to each element of the jagged array and access their elements using the indexing syntax.

17. What is the volatile keyword in C#?

The volatile keyword in C# is used to declare a field that can be accessed by multiple threads. It ensures that the field is read from and written to directly from memory, rather than being cached in registers or CPU caches. The volatile keyword prevents certain optimizations that could cause issues in multithreaded scenarios.

Example:

C#
class Program
{
    private static volatile bool flag = false;

    static void Main()
    {
        new Thread(WorkerThread).Start();

        Thread.Sleep(1000);  // Wait for a while

        flag = true;  // Change the value from the main thread
    }

    static void WorkerThread()
    {
        while (!flag)
        {
            // Do some work until the flag is set
        }

        Console.WriteLine("Flag is set!");
    }
}

In this example, the flag field is declared as volatile. The WorkerThread method continuously checks the value of flag until it becomes true. Without the volatile keyword, the loop might be optimized by caching the value of flag, leading to an infinite loop even after the main thread sets flag to true. The volatile keyword ensures that the value of flag is read from the memory directly, preventing such optimizations.

18. What is Reflection in C#?

Reflection in C# is a feature that allows examining and manipulating the metadata of types at runtime. It provides the ability to inspect classes, methods, properties, and other members of types, even if their definitions are not known at compile time. Reflection is commonly used for building tools, frameworks, and libraries that require dynamic behavior.

Example:

C#
using System;
using System.Reflection;

class Program
{
    static void Main()
    {
        Type type = typeof(MyClass);
        MethodInfo method = type.GetMethod("MyMethod");
        PropertyInfo property = type.GetProperty("MyProperty");

        Console.WriteLine("Method: " + method.Name);
        Console.WriteLine("Property: " + property.Name);
    }
}

class MyClass
{
    public void MyMethod()
    {
        Console.WriteLine("Hello, Reflection!");
    }

    public string MyProperty { get; set; }
}

In this example, we use reflection to obtain metadata about the MyClass type. We get the MyMethod method and the MyProperty property using the GetMethod() and GetProperty()

methods of the Type class, respectively. We can then access and use these members dynamically at runtime. In this case, we simply display the names of the method and property.

19. What is the checked keyword in C#?

The checked keyword in C# is used to explicitly enable overflow checking for integral arithmetic operations. By default, C# performs unchecked arithmetic, which means that it does not throw exceptions when overflow or underflow occurs. However, by using the checked keyword, you can enforce overflow checking and throw an exception if an arithmetic operation overflows.

Example:

C#
int x = int.MaxValue;
int y = 2;

try
{
    int result = checked(x * y);
    Console.WriteLine(result);
}
catch (OverflowException ex)
{
    Console.WriteLine("Overflow occurred: " + ex.Message);
}

In this example, we multiply x and y, where x is set to the maximum value of an int. Since the result exceeds the maximum value of int, an OverflowException is thrown. By using the checked keyword, we can catch the exception and handle the overflow condition.

20. What is a Predicate in C#?

In C#, a predicate is a delegate or a lambda expression that represents a function or condition that tests whether an object satisfies a specific criteria. Predicates are commonly used for filtering or searching elements in collections or when working with LINQ queries.

Example:

C#
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        List<int> numbers = new List<int> { 1, 2, 3, 4, 5 };

        // Using a predicate with FindAll() method
        List<int> evenNumbers = numbers.FindAll(IsEven);

        Console.WriteLine("Even numbers:");
        foreach (var number in evenNumbers)
        {
            Console.WriteLine(number);
        }
    }

    static bool IsEven(int number)
    {
        return number % 2 == 0;
    }
}

In this example, we define a predicate IsEven that tests whether a number is even. We use the FindAll() method of the List<T> class to filter the numbers and find all the even numbers. The IsEven predicate is passed as an argument to FindAll() to specify the condition for filtering. The output will be the list of even numbers in the numbers collection.

21. What are Async and Await in C#?

Async and await are keywords in C# used for asynchronous programming, which allows you to write code that can execute concurrently and efficiently handle I/O-bound or long-running operations without blocking the main thread.

  • async: The async keyword is used to define an asynchronous method. An asynchronous method can use the await keyword inside its body to asynchronously wait for the completion of other asynchronous operations.
  • await: The await keyword is used to indicate a point in an asynchronous method where the execution should asynchronously wait for the completion of a task or an awaitable object. The await keyword can only be used inside an asynchronous method.

Example:

C#
using System;
using System.Net.Http;
using System.Threading.Tasks;

class Program
{
    static async Task Main()
    {
        string url = "https://api.example.com/data";
        string result = await GetDataFromUrl(url);

        Console.WriteLine("Data from URL: " + result);
    }

    static async Task<string> GetDataFromUrl(string url)
    {
        HttpClient client = new HttpClient();
        string data = await client.GetStringAsync(url);
        return data;
    }
}

In this example, the Main method is defined as an asynchronous method using the async keyword. It calls the GetDataFromUrl method asynchronously using the await keyword. The GetDataFromUrl method internally uses HttpClient to make an asynchronous HTTP request and retrieves the data as a string. The await keyword allows the execution to asynchronously wait for the completion of the HTTP request.

22. What is the use of the yield keyword in C#?

The yield keyword in C# is used in iterator methods to create an iterator block. It simplifies the implementation of custom iterators by automatically generating the necessary state machine code. The yield keyword is used to return elements one at a time, lazily, without needing to build and return an entire collection at once.

Example:

C#
using System;
using System.Collections.Generic;

class Program
{
    static void Main()
    {
        foreach (var number in GenerateNumbers())
        {
            Console.WriteLine(number);
        }
    }

    static IEnumerable<int> GenerateNumbers()
    {
        for (int i = 1; i <= 5; i++)
        {
            yield return i;
        }
    }
}

In this example, the GenerateNumbers method is an iterator method defined using the yield keyword. It returns a sequence of integers from 1 to 5. When the foreach loop iterates over the sequence, the yield return statement is used to lazily generate and return each number one at a time. This avoids building and returning a complete collection of numbers upfront, improving performance and memory usage.

23. What is a Static Constructor in C#?

A static constructor in C# is a special constructor that is used to initialize the static members of a class. It is called automatically before any static members of the class are accessed or before any instance of the class is created. Static constructors are defined using the static keyword and have no access modifiers or parameters.

Example:

C#
class MyClass
{
    private static int count;

    static MyClass()
    {
        count = 0;
        Console.WriteLine("Static constructor called");
    }

    public MyClass()
    {
        count++;
    }

    public static int GetCount()
    {
        return count;
    }
}

class Program
{
    static void Main()
    {
        MyClass obj1 = new MyClass();
        MyClass obj2 = new MyClass();

        int count = MyClass.GetCount();
        Console.WriteLine("Count: " + count);
    }
}

In this example, the MyClass has a static constructor defined using the static keyword. The static constructor initializes the static member count to 0 and displays a message. Each time an instance of MyClass is created, the count is incremented in the instance constructor. The GetCount() method returns the value of count. When the program runs, the static constructor is called automatically before any instances are created, and the output will be Count: 2.

24. What is the nameof operator in C#?

The nameof operator in C# is used to obtain the name of a variable, type, or member at compile-time as a string. It helps avoid hard-coding names as strings and provides a way to get the names in a type-safe manner. The nameof operator is especially useful when working with reflection or when the name of an entity needs to be used as a string.

Example:

C#
class MyClass
{
    public int MyProperty { get; set; }
}

class Program
{
    static void Main()

{
        Console.WriteLine(nameof(MyClass));  // Output: MyClass
        Console.WriteLine(nameof(MyClass.MyProperty));  // Output: MyProperty
    }
}

In this example, the nameof operator is used to retrieve the names of the MyClass type and its MyProperty property. The operator returns the names as strings at compile-time. This allows for safer and more maintainable code, as any changes to the names will be reflected in the nameof expressions.

25. What is an Object Initializer in C#?

An object initializer in C# is a syntax that allows you to set the initial values of an object’s properties or fields directly at the time of object creation. It provides a concise and convenient way to initialize object members without explicitly invoking multiple property setters or constructors.

Example:

C#
class Person
{
    public string Name { get; set; }
    public int Age { get; set; }
}

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

        Console.WriteLine("Name: " + person.Name);
        Console.WriteLine("Age: " + person.Age);
    }
}

In this example, the Person class has properties Name and Age. The object initializer syntax is used to create an instance of Person and set its properties in a single statement. The Name property is set to “John” and the Age property is set to 30. This eliminates the need for separate property assignments after object creation.

Advanced Questions

1. Explain the difference between “ref” and “out” parameters in C#.

Parameter Type“ref” Parameter“out” Parameter
InitializationRequiredNot Required
UsageCan be both read and written within the methodMust be written before being read within the method
Caller ObligationMust be initialized before passing as an argumentNot required to be initialized before passing as an argument
Return ValueCan modify the value passed as an argument, and the modified value is visible to the callerTypically used for multiple return values, as the method is not required to assign a value before returning
Examplecsharp int Increment(ref int value) { value += 1; return value; }csharp bool TryParse(string input, out int result) { result = 0; return int.TryParse(input, out result); }

2. What is the purpose of the “using” statement in C#? How is it different from “try-finally”?

The “using” statement in C# is used to ensure that a resource is properly disposed of when it is no longer needed. It provides a convenient way to work with objects that implement the IDisposable interface.

The “using” statement is different from the “try-finally” statement in the following ways:

Aspect“using” Statement“try-finally” Statement
PurposeEnsures proper disposal of resourcesEnsures execution of cleanup code
Resource HandlingAutomatically disposes the resource at the end of the “using” blockRequires explicit cleanup code in the “finally” block
Syntaxcsharp using (var resource = new SomeResource()) { // Use the resource }csharp var resource = new SomeResource(); try { // Use the resource } finally { // Cleanup code to dispose the resource }

3. What are the differences between an abstract class and an interface in C#?

AspectAbstract ClassInterface
InstantiationCannot be instantiatedCannot be instantiated
InheritanceSupports single inheritance and can inherit from a class or another abstract classSupports multiple inheritance and can inherit from multiple interfaces
Default ImplementationCan provide default implementation for some or all of its membersCannot provide default implementation for any member
Access ModifiersCan have access modifiers (public, protected, internal, etc.) for its membersAll members are implicitly public
UsageUsed when creating a base class with some common behaviorUsed when defining a contract for multiple unrelated classes
Examplecsharp abstract class Animal { public abstract void Sound(); }csharp interface IAnimal { void Sound(); }

4. How does the “yield” keyword work in C#? Provide an example.

The “yield” keyword in C# is used in an iterator block to create an iterator. An iterator allows you to define a sequence of values that can be enumerated using a foreach loop or other methods that iterate over collections.

Here’s an example of how the “yield” keyword can be used:

C#
public IEnumerable<int> GetEvenNumbers(int max)
{
    for (int i = 0; i <= max; i++)
    {
        if (i % 2 == 0)
        {
            yield return i;
        }
    }
}

// Usage:
foreach (int number in GetEvenNumbers(10))
{
    Console.WriteLine(number); // Output: 0, 2, 4, 6, 8, 10
}

In this example, the GetEvenNumbers method returns an IEnumerable<int> using the “yield” keyword. It generates even numbers up to the specified max value. The method does not execute fully when called, but instead, it returns an iterator that generates the next number in the sequence each time the foreach loop requests the next value.

5. Explain the concept of boxing and unboxing in C#.

Boxing and unboxing are mechanisms in C# to convert between value types and reference types.

  • Boxing: It is the process of converting a value type to an object reference type by encapsulating the value in an instance of the System.Object class or a compatible reference type. This allows value types to be treated as objects and be stored in reference type containers like ArrayList or passed as parameters to methods expecting reference types.
C#
int value = 42; // value type
object boxedValue = value; // boxing
  • Unboxing: It is the process of extracting the value from an object reference type and converting it back to the original value type. This is necessary when retrieving the original value from an object reference.
C#
object boxedValue = 42; // boxed value
int value = (int)boxedValue; // unboxing

Boxing and unboxing involve a performance cost because of the conversion between value and reference types. It is generally recommended to use generics (List<T>, Dictionary<TKey, TValue>, etc.) instead of boxing/unboxing when working with collections of value types.

6. What is the purpose of the “readonly” keyword in C#? When should it be used?

The “readonly” keyword in C# is used to declare a field that can only be assigned a value once, either at the time of declaration or within a constructor of the class. Once assigned, its value cannot be changed.

The purpose of the “readonly” keyword is to ensure that the value of a field remains constant throughout the lifetime of an object, providing immutability and guaranteeing that the field cannot be accidentally modified.

It should be used in scenarios where a field’s value is not intended to change after initialization, such as for constants, configuration values, or other values that should remain fixed. By using the “readonly” keyword, you can make your code more robust and prevent unintended modifications to critical data.

C#
public class Circle
{
    private readonly double radius;
    private readonly double area;

    public Circle(double radius)
    {
        this.radius = radius;
        this.area = CalculateArea();
    }

    public double Radius => radius;
    public double Area => area;

    private double CalculateArea()
    {
        return Math.PI * radius * radius;
    }
}

In this example, the “radius” and “area” fields of the Circle class are marked as “readonly” since they should not be modified once initialized. The values are set within the constructor, and any attempts to modify them elsewhere in the class would result in a compilation error.

7. Describe the difference between “String” and “StringBuilder” in C#.

AspectStringStringBuilder
MutabilityImmutable (cannot be modified)Mutable (can be modified)
Memory AllocationCreates a new string instance for every modificationModifies the same instance without creating new instances
PerformanceInefficient for multiple modifications, as each modification creates a new stringEfficient formultiple modifications, as it avoids unnecessary memory allocations
UsageSuitable for scenarios where the string value is static or requires minimal modificationsSuitable for scenarios where frequent modifications are needed, such as building dynamic strings
Examplecsharp string name = "John";csharp StringBuilder builder = new StringBuilder(); builder.Append("Hello"); builder.Append("World"); string result = builder.ToString();

8. What is the difference between “IEnumerable” and “IQueryable” in C#?

AspectIEnumerableIQueryable
NamespaceSystem.CollectionsSystem.Linq
Data SourceIn-memory collection or any other data sourceTypically used with queryable data sources like databases
Deferred ExecutionExecutes queries in-memory on the client sideExecutes queries on the server side, such as a database
Query CompositionSupports basic LINQ operators like filtering, projection, and orderingSupports advanced query operations and can be extended with provider-specific functionality
Examplecsharp IEnumerable<int> numbers = new List<int> { 1, 2, 3, 4, 5 };csharp IQueryable<int> query = dbContext.Products.Where(p => p.Price > 10);

9. Explain the concept of delegates and events in C#.

Delegates in C# are a type-safe way to define and encapsulate method references. They allow methods to be assigned to variables, passed as arguments to other methods, and invoked dynamically at runtime.

A delegate is declared using the delegate keyword and specifies the signature of the method it can reference. It can then be instantiated with a method that matches the specified signature. Invoking a delegate will execute the underlying method.

Events in C# are a special type of delegates that provide a way to handle and respond to specific occurrences, typically used for asynchronous notifications. They are declared using the event keyword and can be associated with delegate types. Events provide encapsulation and allow subscribers to register and unregister event handlers.

Here’s an example that demonstrates the concept of delegates and events:

C#
// Delegate declaration
public delegate void CalculationPerformedHandler(int result);

// Event declaration
public event CalculationPerformedHandler CalculationPerformed;

// Method that performs a calculation and raises the event
public void PerformCalculation(int a, int b)
{
    int result = a + b;

    // Raise the event
    CalculationPerformed?.Invoke(result);
}

// Event handler method
public void CalculationEventHandler(int result)
{
    Console.WriteLine("Calculation result: " + result);
}

// Usage:
var calculator = new Calculator();
calculator.CalculationPerformed += CalculationEventHandler; // Subscribe to the event
calculator.PerformCalculation(5, 3); // Perform the calculation and trigger the event

In this example, the CalculationPerformedHandler delegate is declared, representing a method that takes an integer parameter. The CalculationPerformed event is declared using this delegate type.

The PerformCalculation method performs a calculation and raises the CalculationPerformed event, invoking any registered event handlers. The CalculationEventHandler method serves as an event handler that will be called when the event is raised.

The event handler is subscribed to the event using the += operator. When PerformCalculation is called, the event is raised, and the event handler is executed, printing the result to the console.

10. What are extension methods in C#? How do they work?

Extension methods in C# allow you to add new methods to existing types without modifying the original type or creating a new derived type. They are a syntactic convenience and provide a way to extend the functionality of types, including classes, structs, interfaces, and enums.

Extension methods are defined in a static class and must be declared as static methods. The first parameter of an extension method specifies the type being extended, preceded by the this keyword. This parameter is typically named this followed by the type name.

Here’s an example that demonstrates the usage of extension methods:

C#
public static class StringExtensions
{
    public static bool IsPalindrome(this string str)
    {
        // Check if the string is a palindrome
        // (code implementation for brevity)
        return true;
    }
}

// Usage:
string word = "level";
bool isPalindrome = word.IsPalindrome();

In this example, the IsPalindrome extension method is added to the string type. It can be invoked on any string instance by using the dot notation, just like any other instance method.

Extension methods can be used to provide additional functionality to existing types, improve code readability, and promote code reuse. However, they should be used judiciously and in cases where the extension method is genuinely applicable to all instances of the extended type.

11. How does garbage collection work in C#? Explain the generations and how objects are finalized.

Garbage collection in C# is an automatic memory management process that releases memory occupied by objects that are no longer reachable or in use by the application. It eliminates the need for manual memory management and helps prevent memory leaks and memory access violations. C# uses a generational garbage collector, which organizes objects into different generations based on their age and lifetime.

The garbage collector consists of three generations: 0, 1, and 2.

  • Generation 0: Newly created objects are placed in Generation 0. It is the youngest generation and gets collected most frequently. Objects that survive garbage collection in Generation 0 are promoted to Generation 1.
  • Generation 1: Objects that have survived garbage collection in Generation 0 are placed in Generation 1. It has a longer lifetime compared to Generation 0. Objects that survive garbage collection in Generation 1 are promoted to Generation 2.
  • Generation 2: Objects that have survived garbage collection in Generation 1 are placed in Generation 2. It has the longest lifetime and contains long-lived objects.

The garbage collector uses a mark-and-sweep algorithm to determine which objects are still reachable. It starts by marking all objects that are referenced from the roots (such as static variables, method parameters, and stack frames) and then traverses the object graph to mark all reachable objects.

Once marking is complete, the garbage collector performs a sweep phase where it reclaims memory from all the objects that were not marked as reachable. This memory is then added back to the available memory pool for future allocations.

Finalization is a separate step that occurs after garbage collection. Objects that have a finalizer (defined with the ~ClassName syntax) are placed in a separate finalization queue. The finalizer thread, also known as the finalization queue thread, runs the finalizers of objects in the queue to perform any necessary cleanup. After the finalizer execution, the objects are reclaimed in subsequent garbage collections.

It’s important to note that finalizers should be used sparingly, as they can introduce delays and impact performance. It’s generally recommended to use the IDisposable pattern with the Dispose method for deterministic cleanup of unmanaged resources.

12. Describe the concept of polymorphism in C# with an example.

Polymorphism is a fundamental concept in object-oriented programming that allows objects of different classes to be treated as objects of a common base class or interface. It enables code to be written in a generic and reusable manner that can work with different types of objects without knowing their specific implementations.

In C#, polymorphism is achieved through inheritance and method overriding. When a derived class inherits from a base class, it can override the base class’s methods with its own implementation. The overridden method can then be invoked on an object of the derived class using a reference of the base class or an interface.

Here’s an example that demonstrates polymorphism in C#:

C#
public class Shape
{
    public virtual void Draw()
    {
        Console.WriteLine("Drawing a shape");
    }
}

public class Circle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a circle");
    }
}

public class Rectangle : Shape
{
    public override void Draw()
    {
        Console.WriteLine("Drawing a rectangle");
    }
}

// Usage:
Shape shape1 = new Circle();
shape1.Draw(); // Output: "Drawing a circle"

Shape shape2 = new Rectangle();
shape2.Draw(); // Output: "Drawing a rectangle"

In this example, the Shape class is the base class that defines a virtual Draw method. The Circle and Rectangle classes inherit from Shape and override the Draw method with their own implementations.

The Draw method is invoked on objects of the Circle and Rectangle classes using references of the base class Shape. The actual implementation of the method is determined at runtime based on the type of the object being referred to (polymorphic behavior).

Polymorphism allows code to be written in a generic way that can operate on objects of different derived classes, providing flexibility, extensibility, and code reusability.

13. What is the purpose of the “lock” keyword in C#? When should it be used?

The “lock” keyword in C# is used to provide mutual exclusion or synchronization in multithreaded scenarios. It ensures that only one thread can enter a specific code block or section at a time, preventing concurrent access to shared resources and avoiding race conditions.

The purpose of the “lock” keyword is to protect critical sections of code where shared data is accessed or modified by multiple threads simultaneously. It ensures that only one thread can execute the locked code block at a time, while other threads wait until the lock is released.

Here’s an example that demonstrates the usage of the “lock” keyword:

C#
private static readonly object lockObject = new object();

public void UpdateCounter()
{
    lock (lockObject)
    {
        // Critical section
        // Access or modify shared data here
    }
}

In this example, the “lock” keyword is used to acquire a lock on the lockObject before entering the critical section of code. Only one thread can execute the code block enclosed in the lock, and other threads that encounter the same lock will wait until the lock is released.

The “lock” keyword ensures that concurrent access to shared resources is properly synchronized, preventing data corruption or inconsistencies due to simultaneous modifications by multiple threads.

It’s important to use the “lock” keyword with caution and ensure that it is used consistently across all code paths that access shared resources. Improper use of locks can lead to deadlocks or performance issues. Additionally, the lock object should be a reference type and shared among the threads that require synchronization.

14. Explain the difference between “var” and “dynamic” in C#.

Aspectvardynamic
Compile-time TypeInferred based on the assigned valueResolved at runtime
Static TypingThe variable is statically typed at compile timeThe variable is dynamically typed at runtime
IntelliSenseFull IntelliSense support based on the inferred typeLimited IntelliSense support, as the type is unknown at compile time
Early BindingResolves method calls and member access at compile timeDefers method resolution and member access until runtime
Examplecsharp var count = 42;csharp dynamic value = 42;

15. Describe the concept of generics in C# and provide an example.

Generics in C# allow you to create classes, interfaces, methods, and delegates that can work with different types without specifying them explicitly. They provide a way to create reusable and type-safe code by introducing type parameters.

Type parameters are placeholders for types that are specified when an instance of a generic type or a generic method is created. They allow you to define functionality that can be applied to different types, providing compile-time type safety.

Here’s an example that demonstrates the concept of generics in C#:

C#
public class Stack<T>
{
    private List<T> items = new List<T>();

    public void Push(T item)
    {
        items.Add(item);
    }

    public T Pop()
    {
        if (items.Count == 0)
            throw new InvalidOperationException("Stack is empty");

        T item = items[items.Count - 1];
        items.RemoveAt(items.Count - 1);
        return item;
    }
}

// Usage:
Stack<int> stack = new Stack<int>();
stack.Push(1);
stack.Push(2);
int poppedItem = stack.Pop(); // Pops 2 from the stack

In this example, the Stack<T> class is a generic class that can work with any type specified by the type parameter T. The Push method allows items of type T to be added to the stack, and the Pop method returns an item of type T from the top of the stack.

When creating an instance of the Stack<T> class, the type parameter T is replaced with the desired type, such as int. This ensures that the stack works with the specified type and provides compile-time type safety.

Generics enable the creation of reusable code and eliminate the need for casting or boxing/unboxing operations when working with collections or algorithms that can operate on different types.

16. How does exception handling work in C#? Explain the use of “try-catch-finally” blocks.

Exception handling in C# provides a mechanism to gracefully handle and recover from runtime errors or exceptional situations. It allows you to write code that can detect and respond to exceptions, preventing application crashes and providing error handling capabilities.

The “try-catch-finally” blocks are used to handle exceptions in C#. The “try” block contains the code that may potentially throw an exception. If an exception occurs within the “try” block, the execution is transferred to the appropriate “catch” block based on the type of the exception.

Here’s the syntax of the “try-catch-finally” blocks:

C#
try
{
    // Code that may throw an exception
}
catch (ExceptionType1 ex1)
{
    // Handle ExceptionType1
}
catch (ExceptionType2 ex2)
{
    // Handle ExceptionType2
}
finally
{
    // Code that always executes, regardless of whether an exception occurred
}

The “catch” block(s) following the “try” block specify the type(s) of exceptions that can be caught. If an exception of the specified type (or its derived type) is thrown, the corresponding “catch” block is executed, allowing you to handle the exception or perform any necessary cleanup.

The “finally” block, if present, is executed regardless of whether an exception occurred or was caught. It is commonly used for cleanup operations, such as closing files or releasing resources, ensuring that they are executed even if an exception is thrown.

Here’s an example that demonstrates exception handling using “try-catch-finally” blocks:

C#
try
{
    int result = Divide(10, 0);
    Console.WriteLine("Result: " + result);
}
catch (DivideByZeroException ex)
{
    Console.WriteLine("Error: " + ex.Message);
}
finally
{
    Console.WriteLine("Cleanup code");
}

int Divide(int a, int b)
{
    try
    {
        return a / b;
    }
    catch (DivideByZeroException ex)
    {
        Console.WriteLine("Error: " + ex.Message);
        throw; // Re-throw the exception to the calling code
    }
}

In this example, the “try” block attempts to divide two numbers and assign the result to the “result” variable. Since division by zero is an invalid operation, it throws a “DivideByZeroException” within the “Divide” method.

The “catch” block catches the “DivideByZeroException” and handles it by printing an error message. The “finally” block is always executed, regardless of whether an exception occurred or not. Exception handling allows you to gracefully handle exceptional conditions, log errors, provide appropriate feedback to users, and ensure that necessary cleanup operations are performed.

17. What are attributes in C#? How can they be used?

Attributes in C# are declarative tags that provide additional metadata or behavior to various program elements, such as classes, methods, properties, parameters, and more. They allow you to add information or annotations to your code, which can be accessed and utilized at compile-time or runtime.

Attributes are defined using the [AttributeName] syntax, where AttributeName represents the specific attribute name. They can take parameters and arguments to provide additional information.

Attributes can be used for various purposes, including:

  1. Adding additional information: Attributes can be used to add descriptive information or annotations to program elements. Examples include [Obsolete] to mark deprecated code or [Serializable] to indicate that a class can be serialized.
  2. Controlling compilation or runtime behavior: Certain attributes, such as [Conditional], can control the inclusion or exclusion of code during compilation based on conditions. Attributes like [MethodImpl] can affect the runtime behavior of methods.
  3. Customizing code generation: Attributes can be used to customize the code generation process. For example, the [DllImport] attribute is used to specify a method in an external DLL.

Attributes can be accessed and processed at compile-time or runtime using reflection. You can retrieve attribute information and use it to modify the behavior or perform specific actions based on the attribute’s presence or values.

18. Explain the concept of threading in C#. How can you create and manage threads?

Threading in C# refers to the ability to execute multiple threads of execution concurrently within a single process. Threads are lightweight units of execution that run independently and allow programs to perform tasks concurrently, making efficient use of available resources.

In C#, you can create and manage threads using the Thread class from the System.Threading namespace. Here are the basic steps to create and manage threads:

  1. Create a new Thread object and specify the method that will be executed by the thread.
  2. Optionally, set thread properties such as name, priority, or background status.
  3. Start the thread using the Start method.
  4. The thread will execute the specified method concurrently with other threads in the application.
  5. If needed, use synchronization mechanisms like locks or other thread-safe constructs to coordinate access to shared resources.
  6. Use methods like Join or Sleep to control the execution flow and synchronization between threads.
  7. Terminate the thread gracefully by allowing the method to complete or by using synchronization constructs like CancellationToken or ManualResetEvent to signal thread termination.

Here’s an example that demonstrates creating and managing a thread:

C#
using System;
using System.Threading;

public class ThreadExample
{
    public static void Main()
    {
        // Create a new thread and specify the method to be executed
        Thread thread = new Thread(CountNumbers);

        // Start the thread
        thread.Start();

        // Main thread continues its execution
        for (int i = 1; i <= 5; i++)
        {
            Console.WriteLine("Main thread: " + i);
            Thread.Sleep(1000);
        }

        // Wait for the other thread to complete
        thread.Join();

        Console.WriteLine("Main thread finished");
    }

    public static void CountNumbers()
    {
        for (int i = 1; i <= 5; i++)
        {
            Console.WriteLine("Secondary thread: " + i);
            Thread.Sleep(1000);
        }

        Console.WriteLine("Secondary thread finished");
    }
}

In this example, a new thread is created using the Thread class, and the CountNumbers method is specified as the target method to be executed by the thread. The main thread starts the new thread using the Start method and continues its own execution. Both the main thread and the secondary thread run concurrently and print their respective messages.

Synchronization is not explicitly handled in this example, but in real-world scenarios, you might need to use synchronization constructs to coordinate access to shared resources or ensure thread safety.

19. Describe the concept of reflection in C#. How can it be used?

Reflection in C# is a powerful mechanism that allows you to inspect and manipulate types, members, and metadata at runtime. It provides a way to examine and interact with the structure, behavior, and attributes of types dynamically.

Reflection can be used to perform various tasks, including:

  1. Retrieving type information: You can use reflection to retrieve information about types, such as their name, base type, implemented interfaces, properties, methods, and more.
  2. Creating instances dynamically: Reflection allows you to create instances of types dynamically, even when the type is not known at compile time. This can be useful for scenarios such as plugin systems or frameworks that require dynamic object creation.
  3. Invoking methods dynamically: Reflection enables you to invoke methods on objects dynamically, even when the method is not known at compile time. This can be useful for scenarios where the method to be invoked is determined at runtime.
  4. Accessing and modifying members: Reflection allows you to access and modify fields, properties, and events dynamically, even if their names are not known at compile time. This can be useful for scenarios where you need to work with dynamically loaded types or when building generic frameworks.
  5. Applying attributes and custom attributes: Reflection enables you to examine and apply attributes to types and members at runtime. This can be useful for scenarios where you need to create extensible or configurable systems that use attributes for metadata or behavior.

Reflection is achieved through the System.Reflection namespace, which provides classes and methods to access type information and perform dynamic operations. Key types include Type, MethodInfo, FieldInfo, PropertyInfo, and Attribute.

It’s important to note that reflection should be used judiciously and is typically associated with performance overhead. It’s recommended to use reflection sparingly and prefer compile-time knowledge whenever possible.

20. What is the purpose of the “async” and “await” keywords in C#? How do they work?

The “async” and “await” keywords in C# are used to create asynchronous code that can execute concurrently without blocking the calling thread. They simplify asynchronous programming by providing a more natural and readable way to write asynchronous code that resembles synchronous code.

The “async” keyword is used to declare an asynchronous method, which can perform long-running or I/O-bound operations without blocking the calling thread. An asynchronous method is typically declared with a return type of Task or Task<T>.

The “await” keyword is used to suspend the execution of an asynchronous method until a specified asynchronous operation is complete. It allows the calling thread to continue with other work or be released while waiting for the awaited operation to complete.

Here’s an example that demonstrates the usage of “async” and “await”:

C#
public async Task<string> DownloadDataAsync(string url)
{
    HttpClient client = new HttpClient();
    string data = await client.GetStringAsync(url);
    return data;
}

// Usage:
string result = await DownloadDataAsync("https://example.com");

In this example, the DownloadDataAsync method is declared as an asynchronous method using the “async” keyword. It uses the HttpClient class to download data asynchronously from a specified URL.

The “await” keyword is used to await the completion of the GetStringAsync method, which returns a Task<string>. While awaiting, the calling thread is free to perform other work or be released, allowing for concurrency and responsiveness.

The result of the asynchronous operation is returned as a string. The calling code uses the “await” keyword to asynchronously wait for the completion of the DownloadDataAsync method and retrieve the downloaded data.

The “async” and “await” keywords simplify the process of writing asynchronous code by eliminating the need for manual thread management, callbacks, or explicit creation of Task objects. They provide a more intuitive and structured approach to asynchronous programming in C#.

21. Explain the difference between value types and reference types in C#.

AspectValue TypesReference Types
StorageStored on the stackStored on the heap
Memory AllocationAllocated automaticallyAllocated using “new” keyword
AssignmentCopy of the valueCopy of the reference
Default ValueValue type-specific defaultNull
BehaviorPassed by valuePassed by reference
Examplesint, float, structclass, object, string, array

22. What are indexers in C#? How do they work?

Indexers in C# allow instances of a class or struct to be accessed as if they were arrays. They provide a way to define custom mechanisms for accessing elements or data within an object using index-based notation.

Indexers are declared within a class or struct using the this keyword followed by an index parameter. The type of the index parameter can be any valid C# type, such as an integer, string, or custom type.

Here’s an example that demonstrates the usage of indexers:

C#
public class MyList<T>
{
    private T[] items;

    public MyList(int size)
    {
        items = new T[size];
    }

    public T this[int index]
    {
        get { return items[index]; }
        set { items[index] = value; }
    }
}

// Usage:
MyList<int> list = new MyList<int>(5);
list[0] = 10; // Set the value at index 0
int value = list[0]; // Retrieve the value at index 0

In this example, the MyList<T> class defines an indexer using the this keyword and an integer index parameter. The indexer allows instances of the class to be accessed using square brackets, just like an array.

The indexer provides both a getter and a setter, allowing values to be retrieved or assigned at specific indices. In this case, the indexer is used to store and retrieve integer values. Indexers provide a convenient way to access elements or data within a class or struct using an index-based syntax, making the object behave like a collection or an array.

23. Describe the concept of LINQ in C#. How can it be used to query data?

LINQ (Language Integrated Query) in C# is a powerful query language that allows you to perform queries on various data sources, such as collections, databases, XML documents, and more. It provides a consistent and unified syntax for querying and manipulating data regardless of the underlying data source.

LINQ introduces query expressions, which are similar to SQL statements, to express queries in a readable and declarative manner. It supports a rich set of query operators, such as filtering, ordering, grouping, joining, and aggregating, that can be combined to construct complex queries.

Here’s an example that demonstrates the usage of LINQ to query data:

C#
var numbers = new int[] { 1, 2, 3, 4, 5 };

var evenNumbers = from num in numbers
                  where num % 2 == 0
                  select num;

foreach (var num in evenNumbers)
{
    Console.WriteLine(num);
}

In this example, an array of integers numbers is defined. LINQ is used to construct a query expression that retrieves the even numbers from the array. The from clause specifies the data source (numbers), the where clause filters the data based on the condition, and the select clause projects the selected data.

The result of the query is an IEnumerable<int> containing the even numbers. The foreach loop is used to iterate over the result and print each number.

LINQ can be used with various data sources, including collections, databases (using LINQ to SQL or Entity Framework), XML documents (using LINQ to XML), and more. It provides a unified and expressive way to query and manipulate data, improving code readability and maintainability.

24. What is the purpose of the “sealed” keyword in C#? When should it be used?

The “sealed” keyword in C# is used to restrict inheritance from a class or to prevent further derivation of a class. When a class is marked as sealed, it cannot be inherited by other classes, and no further subclasses can be derived from it.

The purpose of the “sealed” keyword is to provide control over class inheritance and prevent unintended modifications or extensions of a class. It is typically used in the following scenarios:

  1. Preventing inheritance: By marking a class as sealed, you explicitly indicate that it should not be inherited. This can be useful when creating utility classes, data containers, or classes that should not be extended for architectural reasons.
  2. Performance optimizations: Sealing a class allows the compiler to perform certain optimizations, as it knows that the class cannot be overridden or extended. This can result in improved performance, especially for performance-sensitive scenarios.

Here’s an example that demonstrates the usage of the “sealed” keyword:

C#
public sealed class FinalClass
{
    // Class implementation
}

In this example, the FinalClass is marked as sealed, indicating that it cannot be inherited by other classes. Any attempt to derive a subclass from FinalClass will result in a compilation error.

It’s important to note that the “sealed” keyword can only be applied to classes, not to methods or properties. Additionally, it is not necessary to mark a class as sealed unless there is a specific requirement to prevent inheritance or gain performance benefits.

25. Explain the concept of serialization and deserialization in C#. How can it be implemented?

Serialization in C# is the process of converting objects into a format that can be stored, transmitted, or persisted, such as binary, XML, or JSON. It allows objects to be represented as a sequence of bytes or text, which can then be restored or reconstructed at a later time.

Deserialization, on the other hand, is the process of recreating objects from the serialized format. It involves converting the serialized data back into objects that can be used and manipulated in the application.

Serialization and deserialization are commonly used in scenarios such as:

  1. Storing or transmitting object data: Objects can be serialized to a file, database, or network stream, allowing their state to be preserved and restored later.
  2. Inter-process communication: Objects can be serialized and sent across process boundaries to communicate between different components or systems.
  3. Web APIs and services: Objects can be serialized to JSON or XML format for exchanging data with web APIs or services.

C# provides built-in mechanisms for serialization and deserialization, including the BinaryFormatter, XmlSerializer, and DataContractSerializer classes. Additionally, third-party libraries like Newtonsoft.Json (Json.NET) are commonly used for JSON serialization and deserialization.

Here’s an example that demonstrates serialization and deserialization using the JsonConvert class from Json.NET:

C#
using Newtonsoft.Json;

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

public class SerializationExample
{
    public static void Main()
    {
        // Create an instance of the Person class
        Person person = new Person { Name = "John Doe", Age = 30 };

        // Serialize the object to JSON
        string json = JsonConvert.SerializeObject(person);

        Console.WriteLine(json);

        // Deserialize the JSON back to an object
        Person deserializedPerson = JsonConvert.DeserializeObject<Person>(json);

        Console.WriteLine(deserializedPerson.Name);
        Console.WriteLine(deserializedPerson.Age);
    }
}

In this example, a Person class is defined with Name and Age properties. The JsonConvert class from Json.NET is used to serialize the person object to JSON format using the SerializeObject method. The resulting JSON string is printed to the console.

The JSON string is then deserialized back to a Person object using the DeserializeObject method. The deserialized object is assigned to the deserializedPerson variable, and its properties are printed to the console.

Serialization and deserialization provide a flexible and efficient way to store, transmit, and exchange object data in various formats. They allow objects to be persisted or communicated across different platforms and technologies.

26. Describe the concept of partial classes in C#. Why are they useful?

Partial classes in C# allow a class to be divided into multiple files while behaving as if it were defined in a single file. Each part of the partial class is defined in a separate file and is combined by the compiler into a single class during compilation.

Partial classes are useful in the following scenarios:

  1. Code organization: Splitting a class into multiple files can help organize large and complex classes, making them more manageable and maintainable. Different parts of the class can be located in separate files based on their functionality or purpose.
  2. Collaboration: Partial classes allow multiple developers to work on different parts of a class simultaneously, without the need for merging conflicts. Each developer can work on their own file, focusing on a specific aspect of the class.
  3. Code generation: Partial classes are commonly used in code generation scenarios, where one part of the class is auto-generated by a tool or framework, and another part is manually implemented. The generated part can be safely regenerated without affecting the custom implementation.

Here’s an example that demonstrates the usage of partial classes:

File: Employee.cs

C#
public partial class Employee
{
    public string Name { get; set; }
    public int Age { get; set; }
}

File: EmployeeExtensions.cs

C#
public partial class Employee
{
    public void PrintDetails()
    {
        Console.WriteLine("Name: " + Name);
        Console.WriteLine("Age: " + Age);
    }
}

In this example, the Employee class is split into two files: Employee.cs and EmployeeExtensions.cs. The properties are defined in the first file, and the PrintDetails method is defined in the second file.

At compile-time, the compiler combines the two files into a single class definition, allowing the properties and method to be used together as if they were defined in a single file.

Partial classes provide a flexible way to organize and collaborate on code, especially for large and complex classes. They enhance code readability, maintainability, and separation of concerns.

27. What are the different access modifiers in C#? Explain their usage.

C# provides several access modifiers that control the visibility and accessibility of types and members within a class or assembly. These access modifiers specify the level of accessibility from other classes or assemblies. The different access modifiers in C# are:

  1. public: The public access modifier allows unrestricted access to types and members from any class or assembly. They are accessible from anywhere in the application.
  2. private: The private access modifier limits the accessibility to within the containing type. Members marked as private can only be accessed from within the same class or struct.
  3. protected: The protected access modifier allows access to members from the same class or struct and any derived classes. It is used to provide access within a class hierarchy.
  4. internal: The internal access modifier allows access to types and members within the same assembly. They are not accessible from other assemblies.
  5. protected internal: The protected internal access modifier combines the behavior of protected and internal. It allows access to types and members within the same assembly and derived classes, whether they are in the same assembly or different assemblies.

Here’s an example that demonstrates the usage of different access modifiers:

C#
public class MyClass
{
    public int PublicField;
    private int PrivateField;
    protected int ProtectedField;
    internal int InternalField;
    protected internal int ProtectedInternalField;
}

In this example, the MyClass class contains fields with different access modifiers. The PublicField is accessible from anywhere in the application. The PrivateField is only accessible from within the MyClass itself. The ProtectedField is accessible from MyClass and any derived classes. The InternalField is accessible within the same assembly. The ProtectedInternalField is accessible within the same assembly and derived classes.

Access modifiers provide control over the visibility and accessibility of types and members, ensuring proper encapsulation and information hiding. They play a crucial role in designing maintainable and secure code.

28. Explain the concept of asynchronous programming in C#. How can you perform asynchronous operations?

Asynchronous programming in C# allows you to write code that can perform long-running or I/O-bound operations without blocking the calling thread. It enables concurrent execution of multiple tasks, improves responsiveness, and allows efficient utilization of system resources.

Asynchronous programming can be performed in C# using the async and await keywords along with the Task or Task<T> types. Here’s how it works:

  1. Declare a method as async: An asynchronous method is declared using the async modifier, which allows the method to use the await keyword.
  2. Await asynchronous operations: Within the asynchronous method, use the await keyword to indicate points where the execution can be asynchronously paused and resumed. The await keyword is used with methods that return a Task or Task<T>.
  3. Perform asynchronous operations: Asynchronous operations can be performed using asynchronous versions of methods provided by libraries or by wrapping synchronous operations in a Task.Run or Task.Factory.StartNew method.
  4. Return a Task or Task<T>: The asynchronous method should return a Task or Task<T> representing the ongoing asynchronous operation. This allows the calling code to await the completion of the method or continue with other work.

Here’s an example that demonstrates asynchronous programming in C#:

C#
public async Task<string> DownloadDataAsync(string url)
{
    HttpClient client = new HttpClient();
    string data = await client.GetStringAsync(url);
    return data;
}

// Usage:
Task<string> downloadTask = DownloadDataAsync("https://example.com");
// Perform other work here
string result = await downloadTask;
Console.WriteLine(result);

In this example, the DownloadDataAsync method is declared as an asynchronous method using the async modifier. It uses the GetStringAsync method of HttpClient to asynchronously download data from a specified URL.

The await keyword is used to asynchronously wait for the completion of the GetStringAsync method. The method returns a Task<string> representing the ongoing asynchronous operation.

In the calling code, the DownloadDataAsync method is invoked, and the returned Task<string> is assigned to a variable. The program can perform other work while the download operation is in progress. Finally, the await keyword is used to asynchronously wait for the completion of the download task, and the downloaded data is printed to the console.

Asynchronous programming allows you to write responsive and efficient code by avoiding blocking operations and leveraging concurrent execution. It is particularly useful when dealing with I/O-bound operations or when responsiveness is critical, such as in UI applications or server-side processing.

29. Describe the concept of dependency injection in C#. How can it be implemented?

Dependency Injection (DI) is a design pattern in C# that promotes loose coupling and modularity by allowing dependencies to be injected into a class from external sources rather than creating them within the class itself. It enables better separation of concerns and facilitates unit testing and maintainability.

In DI, dependencies are provided to a class through its constructor, properties, or method parameters. The responsibility of creating and managing dependencies is delegated to an external entity known as a dependency injection container or framework.

Dependency injection can be implemented in C# using various approaches:

  1. Constructor Injection: Dependencies are provided through a class’s constructor. The class declares one or more constructor parameters representing its dependencies, and the container resolves and injects the dependencies when creating an instance of the class.
  2. Property Injection: Dependencies are provided through public properties of a class. The class exposes properties that represent its dependencies, and the container sets the property values with the resolved dependencies after creating an instance of the class.
  3. Method Injection: Dependencies are provided through method parameters. The class declares methods that require dependencies as parameters, and the container resolves and injects the dependencies when invoking the methods.

Here’s an example that demonstrates constructor injection:

C#
public class Logger
{
    public void Log(string message)
    {
        Console.WriteLine("Logging: " + message);
    }
}

public class MyClass
{
    private readonly Logger logger;

    public MyClass(Logger logger)
    {
        this.logger = logger;
    }

    public void DoSomething()
    {
        logger.Log("Doing something");
    }
}

// Usage:
Logger logger = new Logger();
MyClass myClass = new MyClass(logger);
myClass.DoSomething();

In this example, the Logger class is a dependency of the MyClass class. The MyClass constructor takes a Logger parameter, and an instance of Logger is injected when creating an instance of MyClass.

The DoSomething method of MyClass uses the injected Logger to log a message. Dependency injection promotes loose coupling, as the class is not responsible for creating or managing its dependencies. It allows for easier testing, as dependencies can be easily mocked or replaced. It also enhances modularity and flexibility, as dependencies can be easily swapped or configured.

Dependency injection frameworks or containers, such as Autofac, Ninject, or Microsoft.Extensions.DependencyInjection, provide automated mechanisms for resolving and injecting dependencies based on configuration or conventions.

30. What are the differences between “Shallow Copy” and “Deep Copy” in C#?

AspectShallow CopyDeep Copy
Copied DataReferences to the original dataNew copies of the original data
ContentsBoth objects share same dataObjects have separate data
Member TypesValue types and reference typesAll types (value and reference)
Memory UsageLess memoryMore memory
ModificationsChanges are reflected in both objectsChanges do not affect each other
ComplexitySimpler and fasterMore complex and time-consuming
Cloning MethodMemberwiseClone() methodCustom implementation

MCQ Questions

1. What does C# stand for?

  • a. Common Syntax Language
  • b. Computer Science
  • c. C Sharp
  • d. Control Structure Language

Answer: c. C Sharp

2. Which of the following is not a primitive data type in C#?

  • a. int
  • b. float
  • c. string
  • d. boolean

Answer: c. string

3. Which keyword is used to define a constant in C#?

  • a. final
  • b. constant
  • c. const
  • d. readonly

Answer: c. const

4. What is the output of the following code snippet?

C#
int x = 5;
int y = x++;
Console.WriteLine(y);
  • a. 4
  • b. 5
  • c. 6
  • d. Error

Answer: b. 5

5. Which access modifier allows a member to be accessed within the same assembly?

  • a. public
  • b. private
  • c. protected
  • d. internal

Answer: d. internal

6. What is the correct way to declare a method that does not return a value in C#?

  • a. void methodName()
  • b. int methodName()
  • c. string methodName()
  • d. bool methodName()

Answer: a. void methodName()

7. What is the purpose of the using statement in C#?

  • a. To include namespaces in the code
  • b. To define a block of code
  • c. To declare variables
  • d. To handle exceptions

Answer: a. To include namespaces in the code

8. What is the output of the following code snippet?

C#
string name = "John";
string greeting = $"Hello, {name}!";
Console.WriteLine(greeting);
  • a. Hello, John!
  • b. Hello, {name}!
  • c. Hello, {John}!
  • d. Error

Answer: a. Hello, John!

9. Which of the following is not a valid way to declare an array in C#?

  • a. int[] numbers;
  • b. int numbers[];
  • c. int[] numbers = new int[5];
  • d. int[] numbers = {1, 2, 3, 4, 5};

Answer: b. int numbers[];

10. What is the output of the following code snippet?

C#
int x = 10;
int y = 20;
int z = (x > y) ? x : y;
Console.WriteLine(z);
  • a. 10
  • b. 20
  • c. 30
  • d. Error

Answer: b. 20

11. What is the difference between a class and a struct in C#?

  • a. A class is a reference type, while a struct is a value type.
  • b. A class can have a default constructor, while a struct cannot.
  • c. A class supports inheritance, while a struct does not.
  • d. All of the above.

Answer: d. All of the above.

12. What is the purpose of the params keyword in C#?

  • a. To declare multiple parameters of the same type
  • b. To pass a variable number of arguments to a method
  • c. To specify optional parameters
  • d. To define properties in a class

Answer: b. To pass a variable number

of arguments to a method

13. What is the output of the following code snippet?

C#
int[] numbers = { 1, 2, 3, 4, 5 };
Array.Reverse(numbers);
foreach (int number in numbers)
{
    Console.WriteLine(number);
}
  • a. 1 2 3 4 5
  • b. 5 4 3 2 1
  • c. 5 1 2 3 4
  • d. Error

Answer: b. 5 4 3 2 1

14. What is the purpose of the out keyword in C#?

  • a. To specify that a parameter is passed by reference
  • b. To indicate that a method does not return a value
  • c. To pass multiple values to a method
  • d. To indicate that a method is overloaded

Answer: a. To specify that a parameter is passed by reference

15. What is the correct way to catch an exception in C#?

  • a. try(exception) { }
  • b. catch(exception) { }
  • c. handle(exception) { }
  • d. try { } catch(exception) { }

Answer: d. try { } catch(exception) { }

16. What is the purpose of the sealed keyword in C#?

  • a. To prevent a class from being inherited
  • b. To prevent a method from being overridden
  • c. To prevent a variable from being modified
  • d. To prevent a namespace from being used

Answer: a. To prevent a class from being inherited

17. What is the output of the following code snippet?

C#
int a = 5;
int b = 10;
int c = a + b++;
Console.WriteLine(c);
  • a. 15
  • b. 16
  • c. 10
  • d. Error

Answer: c. 10

18. What is the difference between throw and throw ex in C#?

  • a. throw preserves the original stack trace, while throw ex resets the stack trace.
  • b. throw rethrows the exception, while throw ex creates a new exception.
  • c. throw provides more information about the exception, while throw ex provides less information.
  • d. There is no difference between them.

Answer: a. throw preserves the original stack trace, while throw ex resets the stack trace.

19. What is the purpose of the finally block in C#?

  • a. To handle exceptions
  • b. To clean up resources
  • c. To specify optional code
  • d. To define constructors

Answer: b. To clean up resources

20. What is the output of the following code snippet?

C#
int[] numbers = new int[3];
numbers[0] = 1;
numbers[1] = 2;
numbers[2] = 3;
Console.WriteLine(numbers.Length);
  • a. 1
  • b. 2
  • c. 3
  • d. Error

Answer: c. 3

21. What is the purpose of the this keyword in C#?

  • a. To reference the current object
  • b. To reference the base class
  • c. To access static members
  • d. To create an instance of a class

Answer: a. To reference the current object

22. What is the output of the following code snippet?

C#
string message = null;
string result = message ?? "Default Message";
Console.WriteLine(result);
  • a. null
  • b. “Default Message”
  • c. Error
  • d. “”

Answer: b. “Default Message”

23. What is the purpose of the base keyword in C#?

  • a. To reference the current object
  • b. To reference the base class
  • c. To create an instance of a class
  • d. To access static members

Answer: b. To reference the base class

24. What is the difference between StringBuilder and String in C#?

  • a. StringBuilder is mutable, while String is immutable.
  • b. StringBuilder is a value type, while String is a reference type.
  • c. StringBuilder can be modified directly, while String cannot be modified directly.
  • d. All of the above.

Answer: a. StringBuilder is mutable, while String is immutable.

25. What is the purpose of the async and await keywords in C#?

  • a. To define asynchronous methods
  • b. To handle exceptions
  • c. To perform type casting
  • d. To create generic classes

Answer: a. To define asynchronous methods