Start here. This is the direct spoken answer to practice first.
Why this question matters
Generics become valuable when their type relationships communicate what an API can safely do. Constraints and variance solve different problems: available operations versus assignment compatibility.
Constraints state what a type argument must support, such as class, struct, notnull, a base type, an interface, or a public parameterless constructor. They let generic code call those guaranteed members without reflection or runtime guessing. Covariance with out allows a producer such as IEnumerable<string> to be used as IEnumerable<object>. Contravariance with in allows a consumer such as IComparer<object> to be used where an IComparer<string> is needed. A generic type is invariant unless its declaration safely permits one of those directions.