Overview
Interfaces in C# under the .NET framework play a crucial role in defining a contract for classes and structs. They specify a set of methods, properties, events, or indexers without implementing them. The importance lies in providing a layer of abstraction, allowing different objects to interact through a shared set of functionalities, promoting loose coupling and enhanced flexibility in application design and development.
Key Concepts
- Contract Definition: Interfaces define a contract that classes or structs must implement, ensuring a consistent use of methods or properties across different types.
- Polymorphism: They enable polymorphism, allowing objects to be treated as instances of their interface types rather than their concrete types.
- Separation of Concerns: By decoupling the implementation and the interface, changes to the implementation do not affect code that uses the interface.
Common Interview Questions
Basic Level
- What is an interface in C# and why is it used?
- Can you write a simple C# interface and a class that implements it?
Intermediate Level
- How do interfaces support polymorphism in C#?
Advanced Level
- Discuss how interfaces can be used to design a loosely coupled system in C#.
Detailed Answers
1. What is an interface in C# and why is it used?
Answer: An interface in C# is a reference type that can contain declarations of methods, properties, events, or indexers. It defines a contract that classes or structs can implement. Interfaces are used to achieve abstraction and multiple inheritances in C#. They allow for the design of flexible and easily maintainable code by decoupling the interface from its implementation.
Key Points:
- Interfaces cannot contain data fields or implementation of methods.
- A class or struct can implement multiple interfaces, enabling multiple inheritances.
- Interfaces enforce a structure that implementing types must follow, ensuring consistency and predictability in behavior.
Example:
public interface IVehicle
{
void Drive();
int Wheels { get; }
}
public class Car : IVehicle
{
public void Drive()
{
Console.WriteLine("Driving a car");
}
public int Wheels => 4; // Property implementation
}
2. Can you write a simple C# interface and a class that implements it?
Answer: Yes, the example below demonstrates a simple interface IGreet
that has one method SayHello()
. The class Person
implements this interface by providing an explicit definition of the SayHello()
method.
Key Points:
- To implement an interface, a class must provide concrete implementations of all the interface's members.
- An implementing class can implement multiple interfaces.
- Interfaces encourage modular and maintainable code design.
Example:
public interface IGreet
{
void SayHello();
}
public class Person : IGreet
{
public void SayHello()
{
Console.WriteLine("Hello, World!");
}
}
3. How do interfaces support polymorphism in C#?
Answer: Interfaces support polymorphism by allowing objects to be treated as instances of an interface rather than their concrete types. This means that multiple classes can implement the same interface but provide different implementations for the interface's members. This allows methods to operate on objects from different classes that implement the same interface, promoting a flexible and extensible design.
Key Points:
- Enables the use of multiple types interchangeably.
- Facilitates the implementation of generic methods and classes.
- Enhances code reusability and scalability.
Example:
public interface IShape
{
void Draw();
}
public class Circle : IShape
{
public void Draw()
{
Console.WriteLine("Drawing a circle");
}
}
public class Square : IShape
{
public void Draw()
{
Console.WriteLine("Drawing a square");
}
}
public class Program
{
public static void DrawShape(IShape shape)
{
shape.Draw();
}
public static void Main()
{
IShape circle = new Circle();
IShape square = new Square();
DrawShape(circle); // Outputs: Drawing a circle
DrawShape(square); // Outputs: Drawing a square
}
}
4. Discuss how interfaces can be used to design a loosely coupled system in C#.
Answer: Interfaces are instrumental in designing loosely coupled systems by abstracting the implementation details of components. By depending on interfaces rather than concrete implementations, components can be changed or replaced without affecting other parts of the system. This approach enhances the system's flexibility, making it easier to update, maintain, or test.
Key Points:
- Promotes the Dependency Inversion Principle (DIP), a core principle of SOLID design.
- Facilitates unit testing by allowing the use of mock objects.
- Supports the development of modular systems with high cohesion and low coupling.
Example:
public interface IDataAccess
{
void SaveData(string data);
}
public class FileDataAccess : IDataAccess
{
public void SaveData(string data)
{
Console.WriteLine($"Saving {data} to a file");
}
}
public class DatabaseDataAccess : IDataAccess
{
public void SaveData(string data)
{
Console.WriteLine($"Saving {data} to a database");
}
}
public class DataManager
{
private readonly IDataAccess _dataAccess;
public DataManager(IDataAccess dataAccess)
{
_dataAccess = dataAccess;
}
public void Save(string data)
{
_dataAccess.SaveData(data);
}
}
In this example, DataManager
depends on the IDataAccess
interface rather than a concrete implementation, allowing for flexible data storage strategies without altering the DataManager
's code.