Hey there, fellow C# enthusiasts! So you've mastered the basics and now you're eager to tackle some more challenging interview questions, huh? Maybe you're a fresher looking to make a strong impression at your first interview or simply looking to build up your C# know-how. Guess what? You've come to the right place, my friend!
In this article, we'll dive into 20 C# interview questions perfect for freshers; ones that are a notch above the basics but still totally doable. Thrown in the mix, you'll find a blend of questions to test your logical thinking, code analysis skills and those ever-so-important programming concepts.
Ready to knock the socks off your interviewers?
Let's roll!
What is a namespace in C#?
Answer
A namespace in C# is a way to group related types and improve the organization of your code. It is used to avoid naming conflicts between different classes or types that may have the same identifier. Namespaces work as a hierarchy, where you can have nested namespaces, and they can span multiple files or even libraries.
For example:
namespace Company.Project
{
class MyClass
{
// Class implementation
}
}
In this example, the MyClass
class is declared inside the Company.Project
namespace.
What is the purpose of the ‘using’ directive in C#?
Answer
The using
directive in C# has two primary purposes:
- Namespace importing:
using
allows you to import a namespace so that you can use the types and members defined in that namespace without specifying their fully qualified name. It makes your code more readable and reduces code complexity. For example:
using System;
class Program
{
static void Main()
{
// Without the 'using' directive, you would need to write: System.Console.WriteLine("Hello, World!");
Console.WriteLine("Hello, World!");
}
}
- Resource management: The
using
statement is used to help you manage resources like file streams, network connections, or database connections that implement the IDisposable interface. Theusing
statement ensures that theDispose()
method is called on the resource when the block of code is exited, releasing the resources properly. For example:
using System.IO;
class Program
{
static void Main()
{
using (StreamReader reader = new StreamReader("file.txt"))
{
// Code to read from the file
} // At this point, the Dispose() method of the StreamReader is automatically called, releasing resources.
}
}
What are reference types in C#?
Answer
Reference types in C# are types that store a reference to the memory location where the data is stored, rather than the actual data itself. When you create objects from classes or use arrays, delegates, interfaces, or most built-in data structures from the .NET Framework, you are working with reference types. Unlike value types, reference types are allocated on the heap, and the garbage collector manages their memory.
Key aspects of reference types:
- When you pass a reference type as a method parameter or assign it to another variable, both variables will refer to the same memory location. Thus, modifications in one variable will affect the other one.
- Reference types can have a
null
value, which means they don’t reference any memory location or object.
class Person
{
public string Name { get; set; }
}
Person person1 = new Person { Name = "John" };
Person person2 = person1; // Both person1 and person2 reference the same object in memory
person2.Name = "Jane"; // Changing person2.Name also updates person1.Name, as they both refer to the same object
How do you declare a method in C#?
Answer
A method in C# is a block of code that performs a specific task and can be called as many times as needed. To declare a method, you need to specify its access level, return type, method name, and a parameter list (if applicable) within the class. Here’s an example:
public class Calculator
{
public int Add(int a, int b)
{
int sum = a + b;
return sum;
}
}
In this example, we declare a method named Add
with the public access modifier, an int return type, and two parameters of type int. The method calculates the sum of the two numbers and returns the result.
What are the basic access modifiers available in C#?
Answer
Access modifiers in C# control the visibility and accessibility of class members (methods, properties, fields, etc.) and classes themselves. The basic access modifiers available in C# are:
- public: Members and classes with the
public
access modifier are accessible from any code inside the same assembly or another assembly that references it. - private: Members declared as
private
can only be accessed within the same class. They are not visible to other classes, even within the same assembly. By default, class members areprivate
if no access modifier is specified. - protected: Members declared with the
protected
access modifier can be accessed within the same class, as well as within derived classes. They are not visible to other non-derived classes in the same assembly. - internal: Members and classes with the
internal
access modifier can be accessed anywhere within the same assembly but are not visible to other assemblies. - protected internal: Members declared with the
protected internal
access modifier are accessible within the same assembly and by derived classes in another assembly.
public class MyClass
{
public int PublicMember;
private int PrivateMember;
protected int ProtectedMember;
internal int InternalMember;
protected internal int ProtectedInternalMember;
}
What is a constructor in C#?
Answer
A constructor is a special method in a class that is called when an object of that class is created. Constructors have the same name as the class and don’t have an explicit return type. Constructors can be overloaded, which means you can have multiple constructors within a class with different parameter lists.
Constructors are used to:
- Initialize the object’s state (set default values for properties, fields, etc.).
- Allocate resources, such as memory or network connections.
- Verify input parameters or ensure that the object is in a consistent state.
Example:
public class Person
{
public string Name { get; set; }
public int Age { get; set; }
// Default constructor
public Person()
{
Name = "Unknown";
Age = 0;
}
// Parameterized constructor
public Person(string name, int age)
{
Name = name;
Age = age;
}
}
// Usage:
Person person1 = new Person(); // Calls the default constructor
Person person2 = new Person("John", 25); // Calls the parameterized constructor
What are indexers in C#?
Answer
Indexers in C# are class members that allow you to access an object’s elements using index notation, similar to how you access elements in an array or a collection. Indexers provide a more intuitive way to interact with the elements stored in a class, especially when it contains a collection or an array.
To define an indexer, you need to use the this
keyword, the type of the elements you want to access, and implement the get
and set
accessors to access and modify the underlying elements.
Here’s an example of an indexer implementation for a simple custom collection class:
public class MyCollection
{
private int[] items = new int[10];
public int this[int index]
{
get
{
return items[index];
}
set
{
items[index] = value;
}
}
}
// Usage:
MyCollection collection = new MyCollection();
collection[0] = 42; // Uses the indexer's set accessor
int firstItem = collection[0]; // Uses the indexer's get accessor
How do you create custom exception handling in C#?
Answer
To create custom exception handling in C#, you can create a custom exception class that inherits from the built-in System.Exception
class or one of its derived classes. Custom exceptions are useful when the built-in exception classes don’t cover specific error situations in your application.
Here’s an example of a custom exception class:
public class CustomException : Exception
{
public CustomException() : base()
{
}
public CustomException(string message) : base(message)
{
}
public CustomException(string message, Exception innerException) : base(message, innerException)
{
}
}
Now you can throw and catch the CustomException
in your code:
try
{
// Some code that might throw a CustomException
throw new CustomException("This is a custom exception");
}
catch (CustomException ex)
{
Console.WriteLine("Caught a CustomException: " + ex.Message);
}
How can you prevent class inheritance in C#?
Answer
To prevent class inheritance in C#, you can use the sealed
keyword when declaring the class. A sealed
class cannot be inherited by any other class, and it is a compile-time error to try to inherit from a sealed class.
public sealed class NonInheritableClass
{
// Class implementation
}
// This will result in a compile-time error:
public class MyClass : NonInheritableClass
{
}
What is the difference between ‘const’ and ‘readonly’ in C#?
Answer
const
and readonly
are both used in C# to create variables that cannot be changed after they are assigned a value, but they have some differences:
-
const
: - Can only be applied to fields, not properties.
- Must be assigned a value at declaration.
- The value assigned must be a constant expression that can be resolved at compile-time.
-
const
fields are implicitly static, and you cannot use instance members to initialize them.
public class Constants
{
public const double Pi = 3.14159;
}
-
readonly
: - Can be applied to fields and properties.
- Can be assigned a value at declaration or inside a constructor.
- The value assigned can be a constant expression or a non-constant expression that is resolved at runtime.
-
readonly
fields can be either static or instance members, depending on your needs.
public class ReadOnlyFields
{
public readonly int InstanceField;
public static readonly int StaticField;
public ReadOnlyFields(int instanceValue, int staticValue)
{
InstanceField = instanceValue;
StaticField = staticValue;
}
}
Summary of differences:
-
const
is a compile-time constant, and its value cannot depend on runtime conditions.readonly
is a runtime constant, and its value can be determined at runtime. -
const
fields are implicitly static, whilereadonly
fields can be either static or instance members.
What is garbage collection in C#?
Answer
Garbage collection (GC) in C# is an automatic memory management system provided by the .NET Framework. It is responsible for freeing up memory that is no longer being used by the application. The garbage collector keeps track of objects allocated on the heap, determines which objects are no longer accessible, and reclaims the corresponding memory space.
The main benefits of garbage collection are:
- Improved developer productivity by reducing boilerplate code for manual memory management.
- Prevention of memory leaks, as unused memory gets automatically reclaimed.
- Protection against certain programming bugs, like dangling pointers or double-free issues.
C#’s garbage collection is non-deterministic, meaning that you cannot predict when exactly the garbage collector will run. It uses a generational approach, classifying objects into generations to reduce the time and resources spent on garbage collection. The garbage collector will typically run when available memory is getting low or when the system is idle.
While garbage collection can bring benefits, care should still be taken when dealing with heavy resource-consuming objects, like file handlers or database connections. Classes that deal with such resource management should implement the IDisposable
interface to allow deterministic resource release.
Describe object-oriented programming (OOP) in C#. Name the main OOP concepts.
Answer
Object-Oriented Programming (OOP) is a programming paradigm that represents concepts as objects with attributes (fields or properties) and behaviors (methods). The goal of OOP is to produce reusable, maintainable, and modular code by emphasizing the separation of concerns and adhering to OOP principles.
C# is an object-oriented programming language, and it fully supports OOP concepts. The main OOP concepts in C# are:
- Encapsulation: The process of bundling data (attributes) and methods (functions) that operate on the data into a single unit (class). Encapsulation helps to protect the internal state of an object and control access to its attributes.
- Inheritance: The ability of a class to inherit properties, methods, and behaviors from another class (
base
class) to enable code reuse and represent a hierarchy of related objects. In C#, a class can only inherit from a single class but can implement multiple interfaces. - Polymorphism: Polymorphism is the ability of a single interface to represent different types or the ability of a method to have multiple implementations based on derived types. In C#, polymorphism can be achieved through overloading (using the same method name with different parameters in the same type) and overriding (redefining a base class method in a derived class).
- Abstraction: Abstraction is the process of simplifying complex real-world objects and concepts by focusing on their essential features. Abstraction can be achieved in C# by using abstract classes and interfaces to define the common properties and methods that a group of related types must implement.
How can you create a generic class or method in C#?
Answer
A generic class or method in C# is a class or method that can work with any type without the need for explicit type casting or type checking at runtime. Generic types increase the reusability, flexibility, and type safety of your code.
To create a generic class or method in C#, you need to use the angle brackets <T>
to define a type parameter. Here’s an example of a generic class and a generic method:
// A generic class example
public class GenericList<T>
{
// Class implementation using the generic type parameter T
}
// A generic method example
public static T Max<T>(T value1, T value2) where T : IComparable<T>
{
return value1.CompareTo(value2) > 0 ? value1 : value2;
}
In this example, T
is the type parameter for both the generic class GenericList<T>
and the generic method Max<T>(T, T)
. The method also uses a constraint (where T : IComparable<T>
) to ensure that the type parameter T
implements the IComparable<T>
interface.
What is the difference between an abstract class and an interface in C#?
Answer
Both abstract classes and interfaces are used to define abstract entities in C#, but they have some differences:
Abstract Class:
- Can have both abstract and non-abstract (implemented) methods.
- Can have fields (variables), properties, and events.
- Supports constructors and destructors.
- Can have access modifiers for members (public, protected, internal, etc.).
- Supports inheritance; a class can inherit from only one abstract class.
- Can have complete implementations for some methods or properties, but cannot be instantiated directly.
Interface:
- Can only have method, property, and event signatures (no implementation).
- Cannot have fields or constructors.
- No member (methods, properties, events) can have an access modifier (all are implicitly public).
- Supports multiple inheritance; a class can implement multiple interfaces.
- Cannot have any implementation details, just the signatures of the members.
In summary, abstract classes provide a base implementation that can be shared among multiple derived classes, while interfaces define a contract that any implementing class must adhere to. Use an abstract class when you want to provide some default behavior and an interface when you need multiple inheritance or want to strictly define a common functionality contract.
What is the difference between ‘out’ and ‘ref’ parameters in C#?
Answer
Both out
and ref
parameters in C# are used to pass arguments by reference, allowing a method to modify the values of the passed variables outside the method. However, there are some differences between the two:
ref:
- The variable passed as a
ref
parameter must be initialized before passing it to the method. - The method called doesn’t have to assign a value to the
ref
parameter, as it already has a value. Example:
public void Swap(ref int a, ref int b)
{
int temp = a;
a = b;
b = temp;
}
int x = 1, y = 2;
Swap(ref x, ref y); // x = 2, y = 1 after the method call
out:
- The variable passed as an
out
parameter doesn’t need to be initialized before passing it to the method. - The method called must assign a value to the
out
parameter before the method returns. Example:
public void Divide(int a, int b, out int quotient, out int remainder)
{
quotient = a / b;
remainder = a % b;
}
int q, r;
Divide(10, 3, out q, out r); // q = 3, r = 1 after the method call
In summary, use ref
parameters when you want the called method to modify an already initialized variable, and use out
parameters when you want the called method to provide a value that doesn’t depend on the initial value of the passed variable.
Describe the concept of delegates in C#.
Answer
A delegate in C# is a reference type that represents a method with a specific signature. Delegates allow you to treat methods as objects and pass them as arguments, return them from methods, or store them as properties. Delegates provide a way to create function pointers or callbacks, making it possible to develop more flexible and extensible applications.
Delegates are particularly useful when designing event-driven systems, where components must react to other component events or signals without tight coupling. Here’s an example of a delegate declaration, instantiation, and invocation:
// Declare a delegate type with a specific signature
public delegate void MyDelegate(string message);
// A method that matches the delegate signature
public static void Greet(string message)
{
Console.WriteLine("Hello, " + message);
}
// Create an instance of the delegate, assigning the method to it
MyDelegate greetDelegate = new MyDelegate(Greet);
// Invoke the delegate
greetDelegate("World");
C# also supports generic delegates, multicast delegates, and built-in Func<T, TResult>
and Action<T>
delegates (introduced in .NET Framework 3.5) for increased flexibility and usability.
What is the difference between the ‘==’, ‘Equals’, and ‘ReferenceEquals’ methods?
Answer
In C#, ‘==’, ‘Equals’, and ‘ReferenceEquals’ are used to compare objects or values for equality, but with some differences:
- == operator: The ‘==’ operator compares two objects or values for equality. For value types, it checks if the values are equal. For reference types, it checks if the object references are equal, which means they’re pointing to the same object in memory. This behavior can be overridden for custom reference types by overloading the ‘==’ operator.
int a = 10;
int b = 10;
Console.WriteLine(a == b); // True, because the values are equal
string s1 = "hello";
string s2 = new String("hello".ToCharArray());
Console.WriteLine(s1 == s2); // False, because the object references are different
- Equals: The ‘Equals’ method compares two objects for equality by checking their values, not their references. The default implementation of ‘Equals’ for reference types checks reference equality, but it can be overridden in your custom class to provide value-based comparison.
string s1 = "hello";
string s2 = new String("hello".ToCharArray());
Console.WriteLine(s1.Equals(s2)); // True, because the values are equal
- ReferenceEquals: The ‘ReferenceEquals’ method checks if two object references are equal, meaning they point to the same object in memory. It does not compare values. This method cannot be overridden and is useful when you need to perform a strict reference comparison.
string s1 = "hello";
string s2 = new String("hello".ToCharArray());
Console.WriteLine(ReferenceEquals(s1, s2)); // False, because the object references are different
In summary, use the ‘==’ operator for simple value comparisons and reference comparisons, ‘Equals’ when you need to perform a value-based comparison for reference types, and ‘ReferenceEquals’ when you want to specifically compare object references.
Explain the concept of dependency injection in C#.
Answer
Dependency Injection (DI) is a design pattern used in C# and other object-oriented languages that helps to separate the creation and management of object dependencies, increasing the modularity, maintainability, and testability of an application.
In the context of C#, dependency injection is a technique in which an object receives its dependencies (objects, services, or values it relies on) from an external source instead of directly creating, managing, or looking up these dependencies. Dependency injection can be achieved using constructor injection, property injection, or method injection:
- Constructor Injection: The dependencies are passed to the object through its constructor. This is the most common and recommended method of dependency injection because it enforces that the object is created with all required dependencies and in a valid state.
public class UserService
{
private readonly ILogger _logger;
public UserService(ILogger logger)
{
_logger = logger;
}
public void DoSomething()
{
_logger.Log("User Service is doing something.");
}
}
- Property Injection: The dependencies are set through public properties or setters of an object. This technique is used when the dependency is optional or can be changed during object lifetime.
public class UserService
{
public ILogger Logger { get; set; }
public void DoSomething()
{
Logger?.Log("User Service is doing something.");
}
}
- Method Injection: The dependencies are passed to an object through a method. This technique is suitable when an object requires a specific dependency for just one method and not for the entire lifecycle of the object.
public class UserService
{
public void DoSomething(ILogger logger)
{
logger.Log("User Service is doing something.");
}
}
In C#, popular dependency injection frameworks like Autofac, Unity, or built-in .NET Core Dependency Injection can be used to manage and resolve object dependencies.
How does C# support multiple inheritances?
Answer
C# does not directly support multiple inheritance for classes. A class in C# can inherit from only one base class. However, C# allows multiple inheritance through interfaces, as a class can implement multiple interfaces.
When a class implements multiple interfaces, it essentially inherits the behavior of each interface, and each interface can be considered as a contract that the class must adhere to. This approach to multiple inheritance enables flexibility while avoiding the complexity and ambiguity that can arise from multiple class inheritance.
Example:
public interface IEngine
{
void StartEngine();
}
public interface IDriver
{
void Drive();
}
public class Car : IEngine, IDriver
{
public void StartEngine()
{
Console.WriteLine("Car engine started.");
}
public void Drive()
{
Console.WriteLine("Car is being driven.");
}
}
// Usage:
Car car = new Car();
car.StartEngine();
car.Drive();
In this example, Car
implements the IEngine
and IDriver
interfaces to provide multiple inheritance-like functionality.
What is method overloading in C#?
Answer
Method overloading is a feature in C# that allows a class to have multiple methods with the same name, but different parameter lists. The correct method is called at compile time based on the arguments provided. Method overloading is a way to achieve compile-time polymorphism. The return type of the methods doesn’t factor into overloading. The method signature, i.e., the method name and parameter list, must be unique.
Here’s an example:
public class Calculator
{
public int Add(int x, int y)
{
return x + y;
}
public double Add(double x, double y)
{
return x + y;
}
public int Add(int x, int y, int z)
{
return x + y + z;
}
}
In the example above, we’ve overloaded the Add
method with different parameter lists.
That's it, folks! These 20 for freshers C# questions are just the beginning.
Stick with me, as I'll be sharing more C# interview questions, tailored for all skill levels - from total newbies to seasoned pros.
Don't forget to follow, so you're always in the loop. Let's ace C# together!
Keep on coding, my friends!
Top comments (3)
Very cool.
In newer versions of C#, you can use file-scope namespacing too, e.g.
This means everything in the file is in the same namespace and is quite useful to reduce nesting by one layer (if everything indeed should be in the same namespace)
Absolutely, Anthony! The new feature in C# 10 allows you to use file-scope namespaces. It's simpler and makes code cleaner by reducing extra layers. A great tip for better-looking code. Thanks for sharing!
Now interfaces can have static methods. I use them as if they were extension methods, when it is some functionality that I end up using in most implementations, or simply together.