Generics in C# allows us to create type-safe definitions of methods and classes without the actual need to commit to an actual data type. You have worked with generic types in the previous recipe, such as enumerated data types and, soon, lambda expression. Generic types can be extended, as shown in the previous recipe; however, there are a few caveats you should keep in mind when extending generics that we will cover in this recipe.
The following code shows the Serialize
and SerializeUser
extension methods that we are using:
The following code shows the use of these extension methods:
In the first snippet, we have the Serialize
extension method on the generic type T
. The purpose of this extension is to serialize an object to a JSON string. This means that this method can be executed on any type.
The following screenshot shows that the IntelliSense treats this as an object
type:
Be careful of infinite recursions as you may notice that the method is also available to be called again.
When using this extension method, you may find that it can be called on any type, including strings, integers, and so on. This may cause unintended behaviors and could be, potentially, dangerous.
The second code snippet gives us an upgraded and safer version of this method. SerializeUser
utilizes the where
clause to place a condition on the type of generic data type this method is exposed to:
This clause states that this method is only available to those objects that are of type IUser
, or inherits from it. In the previous screenshot, you will notice that this extension method is not available to that data type in the IntelliSense. Even though our parameter entity
is of type T
, the clause tells the compiler it is actually an IUser
type, the IntelliSense will operate accordingly.
The purpose of our SerializeUser
method is to serialize the object of the IUser
type while removing the password from it. In the following image, both methods are available to us in IntelliSense. The compiler sees that obj
is of the User
type which inherits from the IUser
type which tells the IntelliSense to show us SerializeUser
.
The following screenshot shows the IntelliSense being activated and displays the available methods for both the object
and IUser
type:
When working with generics, try to narrow down the types the method will apply to by using the where
clause unless you are certain that the method should apply to every data type available to the compiler.
In this recipe, you have have learned generics behave with extension methods with a few things to watch out for and ways to work around them.