Generic

Generics in C# allow you to defer the data type specification of programming elements in a class or method, until it is actually used in the program. In other words, Generic allows you to write a class or method that can work with any data type.

Simply put, generic is also a data type in C#, it is the same as int, float, string, bool, ..., but the difference here is that generic is a "free" data type. , meaning it can be any data type, depending on the purpose of use, it is like a type representing all remaining data types.

You write specifications for classes and methods, with interchangeable parameters for data types. When the Compiler encounters a constructor for a class or a function call for a method, it generates code to handle that specific data type.

Problem to be solved

Suppose we have a requirement to write a program to add an integer element to a list. Of course, doing that is very easy if we know the class Listin C#. However, I just assume we don't know anything about it. I will implement it as follows:

public class ExampleList
{
    public void Add(int item) { }
}

In the main program we use it as follows:

class TestProgram
{
    static void Main()
    {
        ExampleList list = new ExampleList();
        list1.Add(1);
    }
}

The problem is, I want ExampleList to be able to add many different types of arguments such as: string, object, date, etc. What should we do? If there is no generic, we need to add another List object to support adding other data types, for example:

public class TextList
{
    public void Add(string item) { }
}

public class ObjectList
{
    public void Add(objetc item) { }
}
...

You can see that is a pretty bad solution, assuming there are about 10 data types we need to write more for 10 List objects with the same structure. That's really not good for future maintenance. So what should we do? Generic is what we need in this case:

public class ExampleList<T>
{
    public void Add(T input) { }
}
class TestProgram
{
    private class ListItem { }
    static void Main()
    {
        ExampleList<int> list1 = new ExampleList<int>();
        list1.Add(1);

        ExampleList<string> list2 = new ExampleList<string>();
        list2.Add("Hello world");

        ExampleList<ListItem> list3 = new ExampleList<ListItem>();
        list3.Add(new ListItem());
    }
}

As you can see in the code above, we only need to declare an ExampleList object with an Add method, passed parameter of type T, then you can add an item of type int, string, object or any object. that you declare.

We have added a variable type T to identify the class or function. T allows us to capture the type the user provides, so we can use this information later. You can use T or another letter like X, Y, ...

Generic types

Generic Type Parameters

In the example at the beginning of the article, ExampleList<T> is not actually a Generic Type Parameter, because to use it we need to initialize it. Generic Type Parameter can be understood as a data type that can be used to declare variables, including a type T that is dynamic, meaning we can pass many different data types to it, for example in C# we have Types that can use Generic Type Parameter are:

public int IComparer<T>() { return 0; }
public delegate bool Predicate<T>(T item);
public struct Nullable<T> where T : struct { /*...*/ }

Generic classes

Generic classes encapsulate operations without specifying data types. Most common cases of using generic classes are with collections like: Linked List, hash tables, queues, trees, etc. Processing is like adding, removing items in the basic collection is implemented by the same mechanism regardless of the data type stored in the collection.

When creating your own custom generic classes, important points to consider are:

  • Which types are generalized to type parameters.

    As a rule, the more parameterized you are, the more flexible and reusable your code will be. However, that can also make the code difficult to read and understand for others.

  • What constraints apply to type parameters?

  • Does implement one or more interfaces.

    For example, if you are designing a class that will be used to create collection-based items, you might have to implement an interface likeIComparable<T> where T

The code below is an example of a Generic class:

 class Demo<T>
 {
    T value;

    public Demo(T t)
    {
        value = t;
    }

    public void Write()
    {
        Console.WriteLine(value);
    }
 }

class Program
{
    static void Main()
    {
        Demo<int> test1 = new Demo<int>(10);
        test1.Write();

        Demo<string> test2 = new Demo<string>("Cat");
        test2.Write();

        Console.ReadLine();
    }
}

Generic Interface

It is often useful to define collection classes, or generic classes. For example, IComparable<T>.

The following example demonstrates how to use the Generic interface. Suppose we build a getAll function and save data of Book and Author objects to the DB. We will use a base interface and implementation as below:

class Book
{
    public string Name { get; set; }

    public int Page { get; set; }
}

class Author
{
    public string Name { get; set; }

    public int Age { get; set; }
}

interface IBaseRepository<T>
{
    List<T> getAll();

    T Save(T item);
}

class BaseRepository<T>: IBaseRepository<T>
{
    public List<T> getAll()
    {
        return new List<T>();
    }

    public T Save(T item)
    {
        return item;
    }
}

class Program
{
    static void Main()
    {
        BaseRepository<Book> bookRepository = new BaseRepository<Book>();
        BaseRepository<Author> authorRepository = new BaseRepository<Author>();
        Book book = bookRepository.Save(new Book { Name = "Book1", Page = 100 });
        Author author = authorRepository.Save(new Author { Name = "Author1", Age = 50 });

        Console.WriteLine("Book: {0} - page: {1}", book.Name, book.Page);
        Console.WriteLine("Name: {0} - age: {1}", author.Name, author.Age);


        Console.ReadLine();
    }
}

Generic method

A generic method is declared with type parameters, which can be understood as the type of the parameter is dynamic (not pre-specified until the method is called) as shown in the code below:

class Program
{
    static int Compare<T>(T first, T second)
    {
        if(first.Equals(second))
        {
            return 0;
        }

        return 1;
    }

    static void Main()
    {
        int result1 = Compare(2, 2);
        int result2 = Compare("abc", "def");

        Console.WriteLine(result1);
        Console.WriteLine(result2);

        Console.ReadLine();
    }
}

Last updated