Introduction
Generics is a familiar concept for most developers using .NET. But every so often, the concept of generic classes is slipping through in most application designs. The generic list is a key feature in .NET and developers tend to use it in almost every custom collection. But why not use generic classes as well? The following sections will give you an overall understanding about generic classes, and most probably, it will help you increase the usage of generic classes in your applications wherever necessary.
Creating a Generic Class
We will create a generic class to hold Number
values. This class can be extended later to hold any type of numbers, e.g., int
, decimal
, or double
. Therefore, T
can be any type of number.
public class Number<T> {
public void AddToCollection(T value) {
}
}
And then, you can extend the class to other type of numbers like int
or double
, as shown in the following example:
public class DecimalNumber : Number<double> {
public DecimalNumber() {
}
}
The advantage is that it will give you the exact object type, thus eliminating the type casting.
Generic Class with Multiple Generic Types
You can even create a class with multiple generic types. This will benefit you when you want to create a collection of generic types in a single base class.
Generic Class with a Generic Property
You can create generic classes with generic properties using the where
and new
keywords. The following example is a class inherited/implemented from the DataModelBase
and ILogicBase
class and interface, respectively. By using a where
clause, we can specify the type (T
) which needs to be the property type. The new
keyword creates an instance of the DataModelBase
class by default if no specific type has been provided for T
. The type T
must be inherited from the DataModelBase
class.
public class LogicBase<T> : ILogicBase
where T : DataModelBase, new() {
private T _Model = default(T);
public T Data {
get {
if (_Model == null) {
_Model = new T();
}
return _Model;
}
set { _Model = value; }
}
}
So for the implementation of the above code, first, we have to create a CustomerData
class by inheriting DataModelBase
and CustomerLogic
, by inheriting the LogicBase
class as shown below.
As you can see from the following code block, the CustomerLogic
class has a CustomerData
object as a property. By using the following pattern, we can increase the application extendibility:
Generic Classes in Real World
You can drastically increase the extendibility of an application design by using generic classes and make it easy to maintain. Let's take a look at the following scenario and see how we can achieve this using generic classes.
Saving a customer record into a database: There are certain classes we need to define in order to create a customer record in the database. So for the initial design, we'll create a generic LogicBase
class where the T
type is the ValidationBase
class. ValidatioBase
-- this class is responsible for all the business related validations. The following code block will show you the syntax for the LogicBase
and ValidationBase
classes.
public class ValidationBase {
public virtual bool IsValidName(string name) {
return name.Length > 5;
}
}
public class LogicBase where T : ValidationBase, new() {
private T _Validations = default(T);
public T Validations {
get {
if (_Validations == null) {
_Validations = new T();
}
return _Validations;
}
set { _Validations = value; }
}
}
Now, we'll create a CustomerClass
by inheriting the LogicBase
class and use its IsValidName
method to do business validation.
public class Customer : LogicBase<ValidationBase> {
public void SaveCustomer(string name) {
if (Validations.IsValidName(name)) {
}
}
}
As you can see in the preceding code block, we have used ValidationBase
to validate the name. To have customer specific validations, let us create a CustomerValidation
class by inheriting from the ValidationBase
class.
public class CustomerValidation : ValidationBase {
public override bool IsValidName(string name) {
return name.Length > 10 ;
}
public bool IsValidCustomer() {
return true;
}
}
You can use the CustomerValidation
class to write only the customer specific validations. Modify the Customer
class to use the CustomerValidation
class as shown below:
Here, you will get all the customer validation methods without casting to a CustomerValidation
type.
Conclusion
By using generic classes, you can significantly increase the application design and extendibility. However, you must thoroughly analyse the classes before extending to a generic class. This will eliminate unnecessary complexity in the class structure. The same behavior can be achieved using some of the Design Patterns, but it requires type casting or Reflection. So by using generic classes, you can eliminate the hassle of type casting to some extent.