How Generics Made C# More Powerful
In C# development, Generics are a powerful tool that enable us to create reusable, versatile, and type-safe classes and methods. Let’s explore how Generics work, see practical examples, and understand how constraints on generic types further enhance their utility.
Understanding Generics with a Generic Class Example
Generics are especially useful for creating classes and methods that work with different data types without sacrificing type safety.
Here's an example of how to create a generic class with a property:
public class GenericObject<T>
{
public T Value { get; set; }
public GenericObject(T initialValue)
{
Value = initialValue;
}
public void DisplayValue()
{
Console.WriteLine($"The value is: {Value}");
}
}
// Usage example:
var intObject = new GenericObject<int>(42);
intObject.DisplayValue(); // Output: The value is: 42
var stringObject = new GenericObject<string>("Example");
stringObject.DisplayValue(); // Output: The value is: Example
In the example above, GenericObject<T> is a class that can store any type of data in the Value property. When the class is instantiated, T is replaced by the type specified during initialization, whether it’s int, string, or any other type.
Generics with Key and Value
Generics are also perfect for data structures that work with pairs, like a dictionary that stores a key and its corresponding value.
public class CompositeObject<TKey, TValue>
{
public TKey Key { get; set; }
public TValue Value { get; set; }
public CompositeObject(TKey key, TValue value)
{
Key = key;
Value = value;
}
public void DisplayPair()
{
Console.WriteLine($"Key: {Key}, Value: {Value}");
}
}
// Usage example:
var item = new CompositeObject<int, string>(1, "Item One");
item.DisplayPair(); // Output: Key: 1, Value: Item One
var stringDoubleItem = new CompositeObject<string, double>("Pi", 3.14);
stringDoubleItem.DisplayPair(); // Output: Key: Pi, Value: 3.14
Here, CompositeObject<TKey, TValue> allows us to pair any types for the key and value, making it useful with int, string, double, etc., to create flexible and type-safe pairs of data.
Recommended by LinkedIn
Constraints on Generic Types
When working with Generics, we sometimes need to limit the types that can be used. In C#, generic type constraints specify that a type must, for instance, inherit from a specific class or implement an interface.
Here’s how it works:
public class ConstrainedStorage<T> where T : IComparable<T>
{
public T Value { get; set; }
public ConstrainedStorage(T initialValue)
{
Value = initialValue;
}
public bool Compare(T otherValue)
{
return Value.CompareTo(otherValue) == 0;
}
}
// Usage example:
var intStorage = new ConstrainedStorage<int>(42);
Console.WriteLine(intStorage.Compare(42)); // Output: True
In this example, ConstrainedStorage<T> uses where T : IComparable<T> to restrict T to types that implement the IComparable interface. This ensures that T is comparable and allows us to use the CompareTo method safely, preventing runtime errors.
Summary
Generics bring flexibility, safety, and reusability to C#, enabling:
These practices not only enhance the code but make it safer and more sustainable, simplifying the creation of dynamic and efficient solutions.
CTA: Have you explored the full potential of Generics? What other scenarios do you think they simplify? Share your thoughts!
Senior Frontend Developer | React | Next.js | Svelte | TypeScript | TDD | AWS
6moThanks for sharing
Senior Flutter Developer | iOS Developer | Mobile Developer | Flutter | Swift | UIKit | SwiftUI
6moUseful tips Cássio Huggentobler de Costa! Thanks for sharing.
Senior Data Engineer | Azure | AWS | Databricks | Snowflake | Apache Spark | Apache Kafka | Airflow | dbt | Python | PySpark | Certified
6moValuable post! Thanks for sharing, Cássio Huggentobler de Costa.
Fullstack Engineer | Software Developer | React | Next.js | TypeScript | Node.js | JavaScript | AWS
6moUseful tips
Software Engineer | Software Developer | .Net | C# | Fullstack | Backend | Azure DevOps | Angular
6moVery good