Adapter Pattern in C#: Definition, Use Cases, Pros, Cons, and Examples
The Adapter Pattern in C# is a structural design pattern that converts the interface of one class into another interface that a client expects, allowing incompatible classes to work together.
The Adapter Pattern acts as a bridge between two incompatible interfaces in a C# application. It wraps an existing class and exposes a new interface that the client code can understand and use. This pattern is commonly used when integrating legacy systems, third-party libraries, or external APIs into modern applications without modifying existing source code. The adapter translates requests from the client into calls that the wrapped object can process. By using adapters, developers can improve code reusability, maintainability, and flexibility while reducing changes to existing systems.
Why We Use Adapter Pattern in C#?
We use the Adapter Pattern in C# to solve compatibility problems between classes that were not originally designed to work together. It helps developers integrate old systems with new architectures without changing existing business logic. The pattern also allows applications to use third-party libraries through a unified interface. In enterprise software, adapters reduce tight coupling and make systems easier to maintain and extend.
Main reasons for using it:
• Integrate incompatible interfaces
• Reuse existing classes without modification
• Connect legacy code with modern applications
• Simplify third-party library integration
• Improve maintainability and flexibility
• Reduce dependency between components
When Should We Use Adapter Pattern in C#?
The Adapter Pattern should be used when:
• Two existing classes have incompatible interfaces
• You want to reuse a legacy class in a new system
• You need to integrate third-party APIs or SDKs
• Refactoring existing code is risky or expensive
• Multiple systems communicate using different data formats
• You want to standardize communication between components
Typical programming problems solved by the Adapter Pattern:
• Legacy system integration
• Payment gateway integration
• File format conversion
• API wrapper implementation
• Database provider abstraction
• External service communication
• Hardware/device driver compatibility
Structure of Adapter Pattern in C#
The Adapter Pattern usually contains these components:
| Component | Responsibility |
|---|---|
| Target | Expected interface used by the client |
| Adapter | Converts requests between interfaces |
| Adaptee | Existing incompatible class |
| Client | Uses the target interface |
Adapter Pattern Examples in C#
Example 1: Legacy Logger Integration
A modern application expects an ILogger interface, but an old logging system uses a different method.
using System;
public interface ILogger
{
void Log(string message);
}
public class LegacyLogger
{
public void WriteLog(string text)
{
Console.WriteLine("Legacy Log: " + text);
}
}
public class LoggerAdapter : ILogger
{
private readonly LegacyLogger _legacyLogger;
public LoggerAdapter(LegacyLogger legacyLogger)
{
_legacyLogger = legacyLogger;
}
public void Log(string message)
{
_legacyLogger.WriteLog(message);
}
}
class Program
{
static void Main()
{
ILogger logger = new LoggerAdapter(new LegacyLogger());
logger.Log("Application started.");
}
}
Example 2: Payment Gateway Adapter
An e-commerce system expects a standard payment interface, but different payment providers expose different APIs.
using System;
public interface IPaymentProcessor
{
void Pay(decimal amount);
}
public class PayPalService
{
public void SendPayment(decimal total)
{
Console.WriteLine($"Paid {total} using PayPal.");
}
}
public class PayPalAdapter : IPaymentProcessor
{
private readonly PayPalService _payPalService;
public PayPalAdapter(PayPalService payPalService)
{
_payPalService = payPalService;
}
public void Pay(decimal amount)
{
_payPalService.SendPayment(amount);
}
}
class Program
{
static void Main()
{
IPaymentProcessor processor =
new PayPalAdapter(new PayPalService());
processor.Pay(250.50m);
}
}
Example 3: XML to JSON Data Adapter
A system expects JSON data, but an external service returns XML.
using System;
public interface IJsonData
{
string GetJson();
}
public class XmlService
{
public string GetXmlData()
{
return "John";
}
}
public class XmlToJsonAdapter : IJsonData
{
private readonly XmlService _xmlService;
public XmlToJsonAdapter(XmlService xmlService)
{
_xmlService = xmlService;
}
public string GetJson()
{
string xml = _xmlService.GetXmlData();
// Simulated conversion
return "{ \"name\": \"John\" }";
}
}
class Program
{
static void Main()
{
IJsonData data =
new XmlToJsonAdapter(new XmlService());
Console.WriteLine(data.GetJson());
}
}
Most Common Real-World Use Cases of Adapter Pattern in C#
| Use Case | Description |
|---|---|
| Legacy System Integration | Connecting old enterprise systems to modern applications |
| Payment Gateway Integration | Standardizing different payment provider APIs |
| Third-Party Library Wrappers | Adapting external SDKs to internal interfaces |
| File Format Conversion | XML, JSON, CSV, or binary transformations |
| Database Provider Adapters | Supporting multiple database engines |
| Hardware/Device Drivers | Making devices compatible with software systems |
| Cloud Service Integration | Standardizing AWS, Azure, or Google APIs |
Advantages of Adapter Pattern in C#
1. Improves Reusability
Existing classes can be reused without modifying their source code.
2. Reduces Code Changes
Legacy or third-party code remains untouched.
3. Increases Flexibility
Applications can support multiple incompatible systems.
4. Encourages SOLID Principles
Especially supports:
• Open/Closed Principle
• Single Responsibility Principle
5. Simplifies Integration
External APIs and services become easier to integrate.
6. Better Maintainability
Changes are isolated inside adapter classes.
Disadvantages (Weak Points) of Adapter Pattern in C#
1. Increased Complexity
Additional classes increase system complexity.
2. More Indirection
Extra abstraction layers can make debugging harder.
3. Performance Overhead
Adapters introduce small runtime overhead due to delegation.
4. Too Many Adapters
Large systems may become difficult to manage if adapters are overused.
5. Harder Understanding for Beginners
Developers unfamiliar with design patterns may find the architecture confusing.
Adapter Pattern vs Similar Patterns
| Pattern | Main Purpose | Key Difference from Adapter | Typical Usage |
|---|---|---|---|
| Adapter | Convert incompatible interfaces | Focuses on compatibility | Legacy integration, API wrappers |
| Facade | Simplify a complex subsystem | Provides simplified interface, not conversion | Complex framework abstraction |
| Decorator | Add new behavior dynamically | Adds functionality instead of adapting interfaces | Logging, caching, validation |
| Bridge | Separate abstraction from implementation | Designed upfront for extensibility | Cross-platform systems |
| Proxy | Control access to an object | Focuses on access control and optimization | Lazy loading, security, remote access |
Summary
The Adapter Pattern in C# is one of the most practical structural design patterns for integrating incompatible systems. It enables legacy code reuse, simplifies third-party integrations, and improves software flexibility without changing existing implementations. Although it adds some complexity and abstraction layers, it is extremely valuable in enterprise applications, API integrations, and modernization projects.
Structural Patterns in C#
6. Adapter
7. Bridge
8. Composite
9. Decorator
10. Facade
11. Flyweight
12. Proxy