C# > Advanced C# > Collections and Generics > Generic Methods and Classes
Generic Class with Type Constraint
This example shows a generic class with a type constraint, ensuring that the type used must implement a specific interface. This enforces a certain contract and allows the class to work with types in a predictable way.
Code Snippet
This code defines an interface IPrintable with a single method Print(). The Printer class is a generic class with a type constraint where T : IPrintable. This constraint ensures that the type parameter T must implement the IPrintable interface. The Printer class has a constructor that takes an object of type T and a method PrintItem() that calls the Print() method of the object. The Document class implements the IPrintable interface and provides a concrete implementation of the Print() method. The usage example shows how to create a Document object and a Printer object, and then call the PrintItem() method to print the document content. The commented out line Printer would cause a compile-time error because int does not implement the IPrintable interface, demonstrating the type safety provided by the generic type constraint.
using System;
// Define an interface
public interface IPrintable
{
void Print();
}
// Generic class with a type constraint
public class Printer<T> where T : IPrintable
{
private T _itemToPrint;
public Printer(T itemToPrint)
{
_itemToPrint = itemToPrint;
}
public void PrintItem()
{
_itemToPrint.Print();
}
}
// Concrete class implementing the interface
public class Document : IPrintable
{
public string Content { get; set; }
public Document(string content)
{
Content = content;
}
public void Print()
{
Console.WriteLine($"Printing document: {Content}");
}
}
// Usage Example:
public class Example
{
public static void Main(string[] args)
{
Document myDocument = new Document("Hello, Generic World!");
Printer<Document> documentPrinter = new Printer<Document>(myDocument);
documentPrinter.PrintItem(); // Output: Printing document: Hello, Generic World!
//The following line would cause a compile-time error because 'int' does not implement IPrintable
//Printer<int> intPrinter = new Printer<int>(5); // Compiler Error
}
}
Concepts Behind the Snippet
Real-Life Use Case
Consider a scenario where you have different types of reports (e.g., sales report, financial report) and you want to process them using a generic processing class. You can define an interface IReport with a method GenerateReport(), and then create a generic ReportProcessor class with the constraint where T : IReport. This ensures that the processor can only work with report types that implement the IReport interface.public interface IReport
{
string GenerateReport();
}
public class SalesReport : IReport
{
public string GenerateReport() { return "Sales Report Content"; }
}
public class ReportProcessor
Best Practices
Interview Tip
Be prepared to explain the purpose of type constraints in generics, how they improve type safety, and how they enable code reuse. Also, be ready to discuss the different types of type constraints (e.g., interface constraint, class constraint, constructor constraint).
When to Use Them
Use generic classes with type constraints when you need to ensure that the type used with the generic class implements a specific interface or inherits from a specific base class. This allows you to write code that relies on the functionality provided by the interface or base class, while still maintaining type safety and reusability.
Memory Footprint
The memory footprint of a generic class with type constraints is similar to that of a regular generic class. Each instantiation of the generic class with a specific type parameter creates a new type, which can contribute to code bloat if many different types are used. The memory footprint of the objects stored in the class depends on their size and the number of objects.
Alternatives
dynamic keyword to bypass compile-time type checking. However, this approach sacrifices type safety and can lead to runtime errors.
Pros
Cons
FAQ
-
What happens if I try to create a `Printer` with a type that doesn't implement `IPrintable`?
You will get a compile-time error because the type constraint is not satisfied. -
Can I have multiple type constraints?
Yes, you can have multiple type constraints using thewherekeyword multiple times (e.g.,where T : IPrintable, new()). Note that new() constraint must be the last one if present. -
Why use an interface constraint instead of a concrete class?
Using an interface constraint promotes loose coupling and allows for more flexibility. You can use any type that implements the interface, rather than being restricted to a specific class.