DEV Community

Sele
Sele

Posted on

Generic Programming

Generics and generic programming are some of the most powerful techniques that developers use to write clean and reusable code. In this article, we are going to be looking at what generic programming is, how it can be applied and why you should start maximizing its power. Before we get our hands dirty on some code lets us look into some ground concepts.

What is Generic Programming

Generic programming is a style of programming in which the types of the objects or variables can be specified at a later time. It allows developers to write code in such a way where the types are parameters.
Some advantages of generics include:

  • Code reuse
  • It prioritizes type safety
  • It also reduces the need to cast an object to other types

NOTE: Dynamically typed programming languages do not have generics. For example javascript, you can create a function and the parameters will work with any type. So this concept is mostly for strongly typed programming languages like java and c#.

The implementation is going to be in c# but this concept is widely used in a lot of strongly-typed programming languages. So let’s go on.

Let’s consider a response model for API endpoints. It will be used as a structure for how all our API responses should look like.

public class Response<T> 
    {
        public bool Successful { get; set; }

        public string Message { get; set; }

        public  T  Data { get; set; }
        public  IEnumerable<T>  DataList { get; set; }

    }                                                              

Enter fullscreen mode Exit fullscreen mode

In the code above the <T> stands for the generic parameter. As you will see we would be able to pass any type of our choice to the class instantiation to determine what the type of the variable Data and DataList will be. So let us use the model on a simple API endpoint.

[HttpGet]
        public IActionResult GetItems()
        {
            var result = item.GetItems();
            return Ok(new Response<Item>
            {
                Successful = true,
                DataList = result,
                Data = null,
                Message = "operation sucessfull"
            });
        }
Enter fullscreen mode Exit fullscreen mode

Inside the ok return statement, we pass the response to the item with the response model we created before and passing a type of item (this is an entity type that was created) into the response object inside the arrow ‘<>’ brackets.

public IActionResult Detail(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
            var result = ops.GetSub(id);
            return Ok(new Response<subTasks>
            {
                Data = result,
                Message = "Operation Successful",

            });
        }
Enter fullscreen mode Exit fullscreen mode

Now in the code above you see we used the same response model and passed in the type parameter into the arrow brackets “<>”, but of a different type of ‘subtask’. The response model is very reusable.
This is a very simple way of implementing generics in c#. we can do a lot more, lets look into how generics can improve abstractions. In the next example, we would look into what we call a generic repository.

So let us consider this interface.

public interface IRepository<T> where T: class
    {
        Task<int> Count { get; }
        Task<T> Add(T item);
        Task<bool> Remove(T item);
        Task<IQueryable<T>> GetBy(Func<T, bool> predicate);
        Task<T> Update(T entity);
        Task<T> GetById(object id);

   }
Enter fullscreen mode Exit fullscreen mode

And this is the implementation

private DbContext<IdentityUser> _dbContext;
        public Repository(DbContext<IdentityUser> dbContext)
        {
            _dbContext = dbContext;
        }



        public async Task<T> Add(T item)
        {
            await _dbContext.AddAsync<T>(item);
            int count = _dbContext.SaveChanges();
            if (count < 1)
            {
                return null;
            }
            return item;
        }


        public async Task<IQueryable<T>> GetAll()
        {
            var allItem = await Task.Run(() => _dbContext.Set<T>().AsQueryable());
            return allItem;
        }
        public async Task<T> GetById(object id)
        {
            return await _dbContext.Set<T>().FindAsync(id);
        }

        public async Task<bool> Remove(T item)
        {
            _dbContext.Set<T>().Remove(item);
            int count = await _dbContext.SaveChangesAsync();
            if (count < 1) { return false; }
            return true;
        }

        public async Task<T> Update(T entity)
        {
            _dbContext.Entry<T>(entity).State = EntityState.Modified;
            int count = _dbContext.SaveChanges();
            if (count < 1)
            {
                return null;
            }
            return entity;
        }

    }
Enter fullscreen mode Exit fullscreen mode

The <T> in the methods stand as the type parameters.

public class StudentService : IStudentService {
    private readonly IRepository<student> _repo;
    public StudentService (IRepository<student> repo) {
        _repo = repo;
    }
    public async Task<List<student>> GetAll () {
        var result = await _repo.GetAll ();
        return result;
    }
    public async Task<student> GetById (int id) {
        var res = await _repo.GetById (id);
        return res;
    }
    public async Task < (bool success, string message) > Create (student obj) 
    {
        var resVal = await _repo.Add (entity);
        if (resVal.Id > 1) {
            return (true, "Operation Successful");
        }
        return (false, "Operation not Successful")
    }
    public async Task < (bool success, string message) >Update (student obj) 
    {
        var retVal = await _repo.Update(entity);
        if (retVal.Id > 0) {
            return (true, "Operation Successful");
        }
        return (false, "Operation not Successful")
    }  
}
Enter fullscreen mode Exit fullscreen mode

In the code above the generic repository is been used in another service. The IRepository implementation can be used in other services with the implementation that we specified, because of generics we can have cleaner abstractions by using a simple implementation for all types.
The point is that we can make better abstractions and make the code more reusable and type-safe. So that’s it on this topic, I am looking forward to seeing how you guys use this technique even in different languages. Please if there anything you didn’t understand put it in the comments I am listening, Have a nice day thank you.

Discussion (2)

Collapse
shock451 profile image
Abdullahi Ayobami Oladosu

This is good stuff.
I'd like to see more of your react native stuff!

Collapse
bolu_oladipo_aee99075f805 profile image
Bolu Oladipo

This is good stuff Sele. Keep these rolling!