How Generics Made C# More Powerful

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.


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:

  • Creation of classes and methods that accept any data type.
  • Definition of data pairs with key and value types.
  • Use of type constraints to manage behavior in generics.

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!

Valmy Machado

Senior Frontend Developer | React | Next.js | Svelte | TypeScript | TDD | AWS

6mo

Thanks for sharing

Like
Reply
Gustavo Guedes

Senior Flutter Developer | iOS Developer | Mobile Developer | Flutter | Swift | UIKit | SwiftUI

6mo

Useful tips Cássio Huggentobler de Costa! Thanks for sharing.

Like
Reply
Rafael Andrade

Senior Data Engineer | Azure | AWS | Databricks | Snowflake | Apache Spark | Apache Kafka | Airflow | dbt | Python | PySpark | Certified

6mo

Valuable post! Thanks for sharing, Cássio Huggentobler de Costa.

Like
Reply
Erick Zanetti

Fullstack Engineer | Software Developer | React | Next.js | TypeScript | Node.js | JavaScript | AWS

6mo

Useful tips

Like
Reply
Thiago Henriques Santos

Software Engineer | Software Developer | .Net | C# | Fullstack | Backend | Azure DevOps | Angular

6mo

Very good

Like
Reply

To view or add a comment, sign in

More articles by Cássio Huggentobler de Costa

Insights from the community

Others also viewed

Explore topics