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 thewhere
keyword 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.