You can express the constraints that will be applied to your type parameters in a number of different ways. If a type exposes any kind of public interface, that interface can typically be used to constrain the signature of your type parameters. The sections that follow look at all the different approaches you can take when constraining your type parameters. Specifically, you'll look at how you can use interfaces, classes, and generic types as constraints. You'll also examine the use of a constructor constraint, which is required to enable applying the new operator to your type parameters. Understanding the implications associated with each of these different constraint types is key to grasping all the nuances of applying constraints.
Because, in most cases, you are expected to use interfaces as a way of separating an object's interface from its implementation, an interface provides you with the most natural mechanism for constraining a type. Interfaces also make a nice vehicle for expressing constraint because they allow your generic types to accept any type argument that happens to implement that interface without binding to any single concrete type. The previous examples leveraged this approach, using the IValidator and IPerson interfaces to constrain their type parameters. You can also imagine how many of the standard framework interfaces might make good candidates to be applied as general-purpose constraints.