Why covariance and contravariance do not support value type
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Covariance and contravariance are important concepts in computer science, particularly when dealing with inheritance, polymorphism, and generics in object-oriented programming languages like C# and Java. They determine how subtyping relationships between more complex types are maintained based on their component types. However, these concepts inherently struggle with value types for technical and conceptual reasons.
Understanding Covariance and Contravariance
Covariance
Covariance allows you to use a more derived type than originally specified. This is predominantly utilized in terms of collections or arrays. For example, in C#, if you have a class inheritance hierarchy where Dog is a subtype of Animal, an interface IEnumerable``<Animal>`can convert toIEnumerable`<Dog>``. This enables flexibility in terms where the type system can automatically convert generic parameters to a derived type.
Contravariance
Contravariance, in contrast, allows you to use a less derived type than originally specified. An example is a situation where a method has a parameter of type object, and you pass a parameter of any type derived from object, which is virtually any type in C#.
Challenges with Covariance and Contravariance in Value Types
Value types, unlike reference types, are stored in the stack and hold the actual data. In C# and Java, value types include primitive types like int, double, and user-defined structures.
Technical Limitations
- Boxing and Unboxing:
- Value types, when used in cases requiring polymorphic behavior, have to be boxed into a reference type, typically
object. This conversion incurs performance overhead due to the extra boxing and unboxing operations.
- Non-Inheritance:
- Value types aren't naturally designed for inheritance. All value types inherently extend the base class
objectbut cannot further extend another value type or be extended by a new value type. Consequently, covariance and contravariance break due to this lack of polymorphic extensibility.
- Stack vs Heap:
- Value types usually reside on the stack. Therefore, increasing the flexibility to convert between them would require more complex memory management shifts to heap storage, defeating their performance advantages.
Conceptual Misalignments
- Expected Mutability:
- Covariant and contravariant operations implicitly assume immutability in the involved types, which is not guaranteed in value types. Modifications in a covariant collection of value types could lead to scenarios where the expected operation is applied on a by-value, non-reflective copy.
- Semantics of Immutability:
- Both covariance and contravariance rely heavily on type hierarchies that are compliant with immutability assumptions, not typically suitable for mutable states frequently encountered in value types.
Practical Demonstrations
Example Code
Consider a scenario in C#:

