Hey C# Developer! Do you know where you are? Yes, on Dev.to but more specifically. That’s right! You’re in part 4 of the most comprehensive C# Interview Questions and Answers article series!
Little more to say if you’re coming from a previous article. As I said, let’s take the difficulty level up a notch and without further blah blah, let’s get started!
What are the differences between IEnumerable, ICollection, and IList interfaces in C#?
Answer
IEnumerable, ICollection, and IList are interfaces provided by the .NET framework libraries for working with collections of data. They have the following differences:
IEnumerable:
- Provides the basic functionality to iterate through a collection using a foreach loop.
- It supports only-read access.
- Ideal for situations where you only need to iterate through a collection.
- Defines only one method: GetEnumerator(), which returns an IEnumerator.
ICollection:
- Inherits from IEnumerable.
- Represents a collective group of elements with the addition of functionalities like size, enumeration, and synchronization.
- Adds basic collection manipulation like Add, Remove, and Clear.
- Supports both read and write access.
- Provides properties like Count and IsReadOnly.
- Provides methods like Add, Remove, Clear, and Contains.
IList:
- Inherits from ICollection.
- Represents a list of items that are accessible by index.
- Provides advanced collection manipulation like insert and remove by index.
- Supports random access to elements.
- Contains properties like Item (property to access elements by index) and methods like Insert and RemoveAt.
Explain the concept of partial classes in C#.
Answer
A partial class, denoted with the partial
keyword in C#, is a class whose definition can be split into multiple separate class definitions in the same namespace. All the separate class definitions with the partial
keyword are combined by the compiler into a single class at the compile-time. Partial classes can be used to logically organize a large class or to enhance classes generated by code generators (e.g., for UI or web services).
Advantages of partial classes:
- Provides better maintainability by dividing a large class into multiple logical files.
- Enhances code generation in situations when you need to add your own code to a generated class.
- Allows multiple developers to work on the same class simultaneously without conflicts.
Syntax:
// File1.cs
public partial class MyClass
{
public void Method1() { /* implementation */ }
}
// File2.cs
public partial class MyClass
{
public void Method2() { /* implementation */ }
}
At compile-time, these two files are combined into a single class.
What is boxing and unboxing in C#? Provide examples.
Answer
Boxing and unboxing are operations in C# related to type conversions between value types and reference types.
Boxing is the process of converting a value type to a reference type (System.Object) by wrapping the value type inside a new object and storing it on the heap. The conversion is implicit.
Example:
int value = 42;
object boxed = value; // Boxing
Unboxing is the process of converting a reference type back to a value type by extracting the value from the object and storing it on the stack, after checking its type is compatible. Unboxing requires an explicit type cast.
Example:
object boxed = 42;
int value = (int)boxed; // Unboxing
Note that these operations come with a performance cost as they involve memory allocation and deallocation, particularly when dealing with large data structures or frequent conversions.
How can you implement multithreading in C#?
Answer
Multithreading is a technique for executing multiple threads concurrently to improve performance and responsiveness in applications. In C#, there are several ways to implement multithreading, including:
- Using the
Thread
class from theSystem.Threading
namespace. Creating a new Thread requires passing a method (delegate) as a parameter to its constructor that will be executed when the thread starts.
using System.Threading;
class Program
{
static void Main()
{
Thread thread = new Thread(new ThreadStart(PrintNumbers));
thread.Start();
PrintNumbers();
}
static void PrintNumbers()
{
for (int i = 0; i < 5; i++)
{
Console.WriteLine(i);
}
}
}
- Using
Task
andTask<TResult>
classes fromSystem.Threading.Tasks
. Tasks provide better abstraction and integration with the ThreadPool, eliminating the need to manually manage threads and their resources.
using System.Threading.Tasks;
class Program
{
static void Main()
{
Task task = new Task(PrintNumbers);
task.Start();
PrintNumbers();
task.Wait(); // Optionally wait for the task to complete
}
static void PrintNumbers()
{
// Same as example 1
}
}
- Using the
async
andawait
keywords for asynchronous programming. This approach simplifies working with long-running methods without blocking the calling thread.
class Program
{
static async Task Main()
{
Task longRunningTask = LongRunningOperationAsync();
// Do other work while the long-running task completes
await longRunningTask;
}
static async Task LongRunningOperationAsync()
{
await Task.Delay(5000); // Placeholder for a long-running operation
}
}
Additionally, you can use Parallel
class for parallel operations on collections (e.g., Parallel.ForEach
or Parallel.For
), and ThreadPool
for managing a pool of threads.
What is the concept of anonymous methods in C#?
Answer
Anonymous methods in C# are a way to define inline unnamed methods that can be assigned to delegate instances. They provide a concise way to create small functions without declaring an entire named method. Anonymous methods use the delegate
keyword and do not require a separate method declaration.
Example:
public delegate void DisplayMessage(string message);
class Program
{
static void Main()
{
DisplayMessage display = delegate(string message) // Anonymous method
{
Console.WriteLine(message);
};
display("Hello, Anonymous method!");
}
}
In C# 3.0 and later, anonymous methods can be replaced by lambda expressions, which provide a more concise syntax. The previous example can be rewritten as:
DisplayMessage display = message => Console.WriteLine(message);
What are the different types of delegates in C#?
Answer
Delegates in C# can be divided into three main types:
- Singlecast Delegates: These delegates reference a single method with a matching signature. When the delegate is invoked, it calls the referenced method.
public delegate void DisplayMessage(string message);
class Program
{
static void Main()
{
DisplayMessage display = ShowMessage;
display("Hello, Singlecast delegate!");
}
static void ShowMessage(string message)
{
Console.WriteLine(message);
}
}
- Multicast Delegates: These delegates can reference multiple methods with a matching signature. When the delegate is invoked, it calls all the referenced methods in the order they were added. Multicast delegates are created using the
+=
or-=
operators.
public delegate void DisplayMessage(string message);
class Program
{
static void Main()
{
DisplayMessage display = ShowMessage1;
display += ShowMessage2;
display("Hello, Multicast delegate!");
}
static void ShowMessage1(string message)
{
Console.WriteLine("Message 1: " + message);
}
static void ShowMessage2(string message)
{
Console.WriteLine("Message 2: " + message);
}
}
- Generic Delegates: These delegates use generic type parameters, allowing them to work with multiple types without casting or boxing/unboxing. C# provides three built-in generic delegates:
Func
,Action
, andPredicate
.
-
Func<TResult>
: A delegate that represents a function with a return type. -
Action
: A delegate that represents a void-returning method with no parameters. -
Predicate<T>
: A delegate that represents a method that takes an input parameter and returns a boolean.
Example using Func
and Action
:
class Program
{
static void Main()
{
Func<int, int, int> add = (x, y) => x + y;
Action<string> display = message => Console.WriteLine(message);
int sum = add(10, 20);
display($"Sum: {sum}");
}
}
Explain the principle of encapsulation in C#.
Answer
Encapsulation is a fundamental object-oriented programming principle that refers to the bundling of data (properties) and behavior (methods) within a single class or object. This concept aims to:
- Hide internal implementation details from other parts of the application (encapsulating inner workings).
- Expose a well-defined public interface for communication between objects.
- Easily update or modify the implementation without affecting the external callers.
In C#, encapsulation can be achieved using access modifiers (public, private, protected, internal, protected internal) to restrict access to the class members and by combining fields, properties, and methods inside a class.
Example:
class BankAccount
{
private double _balance; // Private field representing the internal state
public double Balance // Public property exposing the balance with read-only access
{
get { return _balance; }
}
public void Deposit(double amount) // Public method for depositing funds
{
if (amount > 0)
{
_balance += amount;
}
}
public void Withdraw(double amount) // Public method for withdrawing funds
{
if (amount > 0 && _balance >= amount)
{
_balance -= amount;
}
}
}
What is the benefit of using an extension method in C#?
Answer
Extension methods in C# are a way to add new methods to an existing type without modifying the original type, inheriting from it, or using any other type of customization. Extension methods can be useful in situations where you do not have the source code of the type, or you want to extend a sealed (non-inheritable) class. The benefits of using extension methods are:
- Improved code readability and maintainability.
- The ability to extend a class without modifying its code.
- Adding functionality to a class without inheriting from it or using composition.
An extension method is defined as a static method in a static class and uses the this
keyword before the first parameter to specify the type it is extending. Example:
public 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 text = "Hello, extension method!";
string reversedText = text.Reverse();
Console.WriteLine(reversedText);
}
}
Here, the Reverse()
extension method is added to the string
type and can be called as if it was a native method of the string
class.
How does C# support operator overloading?
Answer
Operator overloading in C# allows any custom class or struct to redefine the behavior of certain operators (like +
, -
, *
, ==
, etc.) for its instances. This can provide a more intuitive and natural syntax when working with objects of the custom type.
To overload an operator, you need to define a public static
method in your class with the operator
keyword followed by the operator symbol being overloaded. The method must have the required parameters and return type as specified by the operator being overloaded.
Example:
public class ComplexNumber
{
public double Real { get; set; }
public double Imaginary { get; set; }
public ComplexNumber(double real, double imaginary)
{
Real = real;
Imaginary = imaginary;
}
// Overloading the + operator
public static ComplexNumber operator +(ComplexNumber a, ComplexNumber b)
{
return new ComplexNumber(a.Real + b.Real, a.Imaginary + b.Imaginary);
}
}
class Program
{
static void Main()
{
ComplexNumber a = new ComplexNumber(3, 2);
ComplexNumber b = new ComplexNumber(1, 4);
ComplexNumber sum = a + b; // Uses the overloaded + operator
}
}
What is the use of the ‘base’ keyword in C#?
Answer
The base
keyword in C# is a reference to a class’s immediate parent class (base class) and is used to access its members, including properties, methods, and constructors. The base
keyword is often used in the following scenarios:
- Calling base class constructors: When creating an instance of a derived class, you can call a base class constructor from the derived class constructor using the
base
keyword followed by the constructor parameters.
public class Animal
{
public int Legs { get; set; }
public Animal(int legs)
{
Legs = legs;
}
}
public class Dog : Animal
{
public Dog() : base(4)
{
// Base class constructor is called with the argument 4
}
}
- Accessing base class methods: In a derived class, you may want to access the original implementation of an overridden or shadowed method from the base class. In this case, you can use the
base
keyword followed by the method name.
public class Animal
{
public virtual void MakeSound()
{
Console.WriteLine("The animal makes a sound");
}
}
public class Dog : Animal
{
public override void MakeSound()
{
// Call base class method
base.MakeSound();
Console.WriteLine("The dog barks");
}
}
class Program
{
static void Main()
{
Dog dog = new Dog();
dog.MakeSound(); // Outputs: "The animal makes a sound" followed by "The dog barks"
}
}
By using the base
keyword, you can extend or reuse functionality from the base class while maintaining the inheritance relationship between the classes.
Explain how LINQ queries work in C# and give an example.
Answer
Language Integrated Query (LINQ) is a powerful feature in C# that allows you to query data from various data sources directly from within the C# language.
In LINQ, query operations are performed using familiar language syntax. This makes the query quite readable and easy to understand. LINQ queries can work with anything that implements the IEnumerable interface, such as arrays and collections. Plus, they are statically typed and use IntelliSense, making writing queries error-free.
Here is an example.
List<int> numbers = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
IEnumerable<int> evenNumbers = from num in numbers where num % 2 == 0 select num;
In the above example, evenNumbers
will hold 2, 4, 6, and 8. The query determines the numbers in the input sequence that fulfill the condition (num % 2 == 0), which are even numbers.
What is a Thread Pool in C#? How can it be beneficial than creating a new thread?
Answer
A Thread Pool in C# is a collection of threads that are made available for use by various tasks. Instead of each task creating, starting, and stopping a new thread, tasks can use a thread from the thread pool, greatly improving efficiency.
There are several benefits to using a thread pool:
- Threads in a thread pool are reused, avoiding the overhead of creating a new thread every time.
- The number of threads in the application does not spike up, avoiding the issue of too many threads which can slow down the system.
- The system automatically manages the details of how and when tasks are assigned to threads in the thread pool.
Here is how you can use the ThreadPool:
ThreadPool.QueueUserWorkItem(new WaitCallback(SomeMethod));
In the above line, SomeMethod
is a method that is executed on a separate thread.
What is a Nullable type in C# and how do you use it?
Answer
In C#, Nullable types are value types which can take null values. This capability is crucial because the value types don’t normally take null values. A Nullable type is declared using the ?
symbol after the value type.
For instance, int?
is a Nullable type based on the int
type. You can assign any value which an int
can take, plus an additional null value.
Here is how you can use it:
int? number = null;
In the code above, because int?
is a nullable type, we’re able to assign a null
value to number
.
How does C# manage memory automatically?
Answer
Memory management in C# is handled through the Garbage Collector (GC), an automatically enabled feature in the .NET framework. Here is how GC works:
- When an object is created in .NET, it is stored in the heap memory.
- The GC automatically reclaims the memory taken up by the unused objects, or the ones that are no longer being used by the application.
- The GC does this automatically without the programmer having to write any specific code for it. This automatic feature saves the developer from various potential errors like memory leaks and invalid memory references.
- The GC also compacts the space, thus enhancing memory locality, which in turn increases performance since objects that are referenced frequently end up being located close each other.
How do you use reflection in C# to inspect an object’s properties and methods?
Answer
Reflection in C# is a feature that enables you to obtain type information at runtime, inspect the metadata of types, create and manipulate instances of types, and invoke methods.
Here’s an example of using reflection to get an object’s properties:
Person person = new Person();
Type type = person.GetType();
PropertyInfo[] properties = type.GetProperties();
foreach(PropertyInfo property in properties)
{
Console.WriteLine("Name: {0}, Value: {1}", property.Name, property.GetValue(person, null));
}
In the example above, GetType()
gets the type of the person object, GetProperties()
gets all the properties of that type, then we loop through the properties and for each property name and its value, we write it out to the console.
Can you explain what a Lambda expression is and give an example of its use?
Answer
A lambda expression in C# is an anonymous function or method that can be used wherever delegate types are required. They provide a simple and concise way of defining methods. Lambda expressions use the =>
operator, which can be read as “goes to”.
For example, here is a lambda expression that squares a number:
Func<int, int> square = x => x * x;
Here x
is the input parameter (can be more than one, comma separated) and x * x
is the method body that is returned from the function.
Lambda expressions can be used in a lot of cases, but they are specially useful in LINQ queries, event handling and creating expression trees.
How do you enforce thread safety in C#?
Answer
Thread safety ensures that data shared between threads are shielded from corruption due to the threads being executed in an overlapping manner. In C#, you can enforce thread safety by using various techniques:
- Locks: The
lock
keyword can be used to ensure that a block of code is not executed by more than one thread at the same time.
private static object _lock = new object();
public void UpdateCounter()
{
lock(_lock)
{
counter++;
}
}
- Monitor:
Monitor
class provides exclusive lock similar tolock
keyword.
Monitor.Enter(lockObject);
try
{
// Code to execute...
}
finally
{
Monitor.Exit(lockObject);
}
- Mutexes and Semaphores: Mutex and Semaphore classes can be used to control access over a pool of resources.
- Thread-Safe Collections: .NET provides inherently thread-safe collections like
ConcurrentBag
,ConcurrentQueue
,ConcurrentStack
,ConcurrentDictionary
, etc.
What are the differences between a struct and a class in C#?
Answer
While structs and classes in C# are quite similar, there are many key differences between them:
- Value Type vs Reference Type: The most significant difference is that struct is a value type and class is a reference type. When a struct is assigned to a new variable, a completely new copy of the value is created and changes made to one will not affect the other. However, with classes, assigning an instance of a class to a new variable doesn’t create a new object but creates a new reference to the existing object.
- Inheritance: Structs in C# cannot inherit from a class or struct, but it can implement an interface. But classes in C# can inherit from a class or implement an interface.
- Default Constructor: Structs do not contain a default (parameterless) constructor, and the compiler does not add one. Classes, however, have a default constructor if no constructors are specified.
- Nullability: Structs cannot be null as they are a value type, whereas classes being a reference type can have a null value.
What are Attribute classes and how are they beneficial?
Answer
Attribute classes in C# are a way for adding declarative information to code. It is a kind of metadata that can be added to the assemblies, types, methods, fields etc. at compile time and can be queried at runtime using reflection.
Benefits of Attribute Classes:
- Code readability and maintainability: With attribute classes, you can control behavior of methods or classes without affecting their source code.
- Metadata Access at Runtime: You can access metadata at runtime for affecting the behavior or else for diagnostics / inspection purpose.
- Controlling behavior: Custom attributes can be queried at runtime and accordingly certain behaviors can be enabled or disabled, without hard-coding them.
For example, Conditional attribute in C# is used for conditional compilation:
class Program
{
[Conditional("DEBUG")]
static void DebugMethod()
{
Console.WriteLine("Debug mode");
}
}
In this example, DebugMethod
will only be called if “DEBUG” symbol is defined.
Well, there you have it! Those were the 20 mid-high level C# interview questions. Bet they got your brain cells working, huh? But remember, each challenge faced is another step up the ladder.
Stick around as we continue to navigate through the complex world of C#, with more mind-boggling challenges up our sleeves. Until then, keep learning, keep growing.
Top comments (3)
With respect to the question regarding IEnumerable, ICollection, and IList.. this distinction is only true for the generic types (ICollection, and IList). For the non-generic interfaces the distinction is less obvious, but ICollection is a non-virtual collection which has size and an inherent nature of "storage structure". IList has the ability to modify the collection, AND has inherent order (ie access by index).
Wow! Really interesting!
Great write-up! Clear, detailed, and valuable insights. Excellent work