Generics is a powerful feature that allows us to write type safe and reusable code. Generic class enables us to write code without worrying about actual type and defers the type specification to the consumer class or client. This allows us to keep the internal algorithms same for different types. In essence, Generic allows us to parameterize the type used inside a class.
-
-
- public class Repository<T> : IRepository<T> where T:class
- {
- protected readonly DbContext _context;
- public IEnumerable<T> GetALL() { return _context.Set<T>().ToList<T>(); }
-
- }
-
-
- public interface IRepository<T> where T : class
- {
- IEnumerable<T> GetALL();
- }
-
-
- public static Nullable<T> GetQueryString<T>(string key) where T : IConvertible
- {
- T result = default(T);
-
- if (String.IsNullOrEmpty(HttpContext.Request.Query[key]) == false)
- {
- string value = HttpContext.Request.Query[key].ToString();
-
- try
- {
- result = (T)Convert.ChangeType(value, typeof(T));
- }
- catch
- {
-
- result = default(T);
- }
- }
-
- return result;
- }
-
- public delegate void notify<T>(T target) where T : class;
Generic Constraints
Constraints help us to define the allowed types and also restricts the consumer from improper use of the class.
T should be a value type : class Repository<T> where T:struct
T should be a reference type : class Repository<T> where T:class
T should have a parameter less constructor: class Repository<T> where T:new()
T should inherit from a type : class Repository<T> where T:Entity
T should implement a interface : class Repository<T> where T:IEntity
Multiple Constrains
When we specify multiple constarints, there are some orders that need to be followed. For example, base class constraint should come first and the new() constraint should come last etc.
class Requester<T> where T:Customer,IDiscouts
class Requester<T> where T:Customer,IDiscouts,new()
Different Generics type declaration
unbound type
An unbound Generic type cannot exist as a instantiated object, but only as a system.type reference. In addition to that, it will not have any type arguments specified.
typeof(List <>)
Constructed type
An Constructed Generic type has at least one type argument specified.
class sample <T,T>
class sample <T,string>
class sample <int,string>
Open Generic type
Any type which has a type parameter.
class sample <T,T>
class sample <T,string>
Closed type
class sample <int,string>
Covariance in Generics
This allows a generic class to reuse the logic available in the base class. In addition to that, covariance can be applied to the methods which returns type T.
ContraCovariance in Generics
This allows a generic class to use the logic available in the sub class. In addition to that, covariance can be applied to the methods which return type T.
This is available in .NET 4.0 and enables us to follow two SOLID Principles called Liskove substitution and Interface Segregation Principle.
The following example shows the parent class and sub classes.
- public interface IEntity
- {
- bool IsValid();
- }
- public class User
- {
- public int UserId { get; set; }
- }
-
- public class Customer : User , IEntity
- {
- public int customerId { get; set; }
- public bool IsValid()
- {
- return true;
- }
- }
-
- public class RewardCustomer : Customer:
- {
- public int RewardId { get; set; }
- }
RePositories
- public interface IUserRepository<out T>
- {
- T FindById(int id)
- }
-
- public interface IRewardRepository<in T>
- {
- void AddRewardUser(T entity);
- }
-
- public interface ICustomerRepository<T> : IUserRepository<T>,IRewardRepository<T>
- {
-
- }
Concrete Repository
- public class CustomerRepository <T> : ICustomerRepository<T> where T: class, IEntity
- {
- DbContext _ctx;
- DbSet<T> _set;
- public void AddRewardUser(T entity)
- {
- if (entity.IsValid())
- {
- _set.Add(entity);
- }
- }
- public T FindById(int id)
- {
- return _set.Find(id);
- }
- }
Consumer Class Customer Controller
- private static void AddRewardUser(IRewardRepository<RewardCustomer> customerRepository)
- {
- customerRepository.Add(new RewardCustomer { RewardId = 100 });
- customerRepository.Commit();
- }
Consumer Class User Details Page
- private void GetUser(IUserRepository<User> customerRepository)
- {
- var user = customerRepository.FindById();
-
- }
Consumer Class Customer Details Page
- private void QueryCustomers(ICustomerRepository<Customer> customerRepository)
- {
- var customer = customerRepository.FindById(1);
-
- }