I am studying C# and generics with class constraint like
public class GenericList<T> where T : Employee
I am puzzled because I see nothing generic here. The GenericList list is bound to the Employee's class. It would be same as if we inject Employee from the GenericList's constructor.
Or I am missing something?
CodePudding user response:
I see nothing generic here
That's what the <T>
is after the name of the type (GenericList
). T
is the name of the generic parameter.
The GenericList list is bound to the Employee's class.
T
can be an Employee
, or it can be any type that inherits Employee
, either directly or through another type.
It would be same as if we inject Employee from the GenericList's constructor.
Not really. What you do in the constructor really has nothing to do with the generic parameter. Generics allow you to modify the type. For example, you can have these different variables.
GenericList<Employee> a;
GenericList<FullTimeEmployee> b;
GenericList<PartTimeEmployee> c;
These are all different types.
Dependency injection just works with parameters in the type's constructor. Yes, those parameter can include types that have generic arguments (for example, you can ask for an ILogger<MyService>
), but they T
type you specify doesn't really have a parallel otherwise.
CodePudding user response:
I am puzzled because I see nothing generic here. The GenericList list is bound to the Employee's class.
I agree that it is a bit hard to see the point of the generic type parameter seeing only a part of class declaration here. But imagine 2 following things:
Employee
is a part of type hierarchy and has descendants (Boss : Employee
,MegaBoss : Boss
)GenericList
has methods likeAdd
andGet
.
Without generic parameterization it could look something like:
public class NotActuallyGenericList
{
public void Add(Employee employee) => throw new NotImplementedException();
public Employee Get(int index) => throw new NotImplementedException();
}
which could be used as:
var list = new NotActuallyGenericList();
list.Add(new Employee());
Employee e = list.Get(0);
Now imagine you need to create a list which should contain only bosses (i.e. instances of Boss
type), then:
var onlyBossesList = new NotActuallyGenericList();
onlyBossesList.Add(new Boss());
Boss e = (Boss)onlyBossesList.Get(1); // neither very convenient nor type-safe
onlyBossesList.Add(new Employee()); // not type-safe
While the generic list will be totally fine:
var onlyBossesList = new GenericEmployeeList<Boss>();
onlyBossesList.Add(new Boss());
Boss b = onlyBossesList.Get(1);
// onlyBossesList.Add(new Employee()); // will not compile
public class GenericEmployeeList<T> where T : Employee
{
public void Add(T employee) => throw new NotImplementedException();
public T Get(int index) => throw new NotImplementedException();
}
And adding the constraint will allow to use Employee
-specific methods/properties inside the GenericEmployeeList
(for example use id to build index for GetById
, etc.).
So (see the docs for generic goals), in this case generics allow to achieve code reuse and type safety.