Proxy Pattern is a structural design pattern that introduces an intermediary object — the proxy — to control access to a real object (Real Subject).
Its purpose is to provide an additional level of control without the rest of the application knowing that it is actually working with an “intermediary”.
This pattern is essential when:
-
the real object is expensive to create → lazy loading,
-
we need security or validations,
-
we want optimization of access to external resources,
-
we need to add cache, logging or monitoring.
1. Why do we need the Proxy Pattern?
Sometimes it is inefficient or unsafe to allow direct access to an object.
Classic examples:
-
⏳ The object is hard to initialize (e.g., large image, database connection).
-
🔐 The object must be protected by an access mechanism.
-
🧮 The object is used frequently, but data doesn’t change → caching.
-
📡 Access involves external calls, slow APIs, costly resources.
Proxy Pattern introduces a control layer that can decide:
-
when the object is created,
-
whether access is allowed,
-
whether data is returned from cache,
-
whether access should be logged or monitored.
2. What is the Proxy?
The proxy is an object with the same interface as the real object.
The client does not know the difference — everything is transparent.
Structure:
-
Subject – the common interface
-
RealSubject – the real implementation
-
Proxy – controls access and delegates to RealSubject
3. Common examples of proxies in .NET
✔ Virtual Proxy
Delays creation of the real object → lazy loading.
✔ Protection Proxy
Controls access → authorization / security.
✔ Cache Proxy
Stores results → performance.
✔ Remote Proxy
Access to remote resources → e.g., WCF, microservices.
4. Simple example in C# – Lazy Loading with Virtual Proxy
Purpose: load a large file (e.g., image) only when it is actually needed.
Subject
public interface IImage
{
void Display();
}
Real Subject
public class RealImage : IImage
{
private readonly string _fileName;
public RealImage(string fileName)
{
_fileName = fileName;
LoadFromDisk();
}
private void LoadFromDisk()
{
Console.WriteLine($"Loading image: {_fileName}");
}
public void Display()
{
Console.WriteLine($"Displaying image: {_fileName}");
}
}
Proxy (Virtual Proxy)
public class ImageProxy : IImage
{
private readonly string _fileName;
private RealImage? _realImage;
public ImageProxy(string fileName)
{
_fileName = fileName;
}
public void Display()
{
if (_realImage == null)
{
_realImage = new RealImage(_fileName);
}
_realImage.Display();
}
}
Usage
IImage img = new ImageProxy("large_photo.png");
// the image is NOT loaded yet
Console.WriteLine("The image will be loaded only at Display.");
img.Display(); // load + display
img.Display(); // display only (no reload)
Result:
-
High efficiency
-
Delayed creation
-
Reduced memory consumption
5. Example: Proxy for security (Protection Proxy)
We control access to a service only for authorized users.
Subject
public interface IDocumentService
{
void ReadDocument();
}
Real Subject
public class DocumentService : IDocumentService
{
public void ReadDocument()
{
Console.WriteLine("Reading confidential document...");
}
}
Secure Proxy
public class SecureDocumentProxy : IDocumentService
{
private readonly DocumentService _documentService = new();
private readonly string _userRole;
public SecureDocumentProxy(string userRole)
{
_userRole = userRole;
}
public void ReadDocument()
{
if (_userRole != "Admin")
{
Console.WriteLine("Access denied!");
return;
}
_documentService.ReadDocument();
}
}
Usage
IDocumentService service = new SecureDocumentProxy("User");
service.ReadDocument(); // Access denied!
service = new SecureDocumentProxy("Admin");
service.ReadDocument(); // OK
6. Advantages
✔ Full control over access
Security, authentication, restrictions.
✔ Lazy loading
Creation only when needed.
✔ Caching and optimization
No duplicated effort for identical results.
✔ Flexible and extensible system
We can introduce logging, auditing, metrics without modifying the real object.
7. Disadvantages
✖ Increases architectural complexity
An additional layer.
✖ The proxy can become a bottleneck
If processing in the proxy is too heavy.
8. When to use Proxy Pattern in .NET?
-
Access to large files (images, documents).
-
Costly API calls → caching.
-
Objects with heavy initialization.
-
Services that require authorization.
-
Remote / distributed resources.
-
Logging / auditing for sensitive actions.