Understanding C# Covariance And Contravariance (8) Struct And Void
Understanding C# Covariance And Conreavariance:
- Understanding C# Covariance And Contravariance (1) Delegates
- Understanding C# Covariance And Contravariance (2) Interfaces
- Understanding C# Covariance And Contravariance (3) Samples
- Understanding C# Covariance And Contravariance (4) Arrays
- Understanding C# Covariance And Contravariance (5) Higher-order Functions
- Understanding C# Covariance And Contravariance (6) Typing Issues
- Understanding C# Covariance And Contravariance (7) CLR
- Understanding C# Covariance And Contravariance (8) Struct And Void
Part 1 mentioned that variances do not work with struct and void.
Struct
When we say Derived object “is a” Base object, it means the reference to a Derived object can be considered as a reference to a Base object.
But struct is value type. On the virtual machine described by CLI, when passing a struct value to a method receiving struct parameter, that value is copied and pushed to the stack. When returning a struct value from a method, that item is placed on the stack. We are not working with references.
Void
The scenario of void looks special. On Microsoft Connect. Someone is asking for “Covariant return types should include void –> anything”.
It looks like anything can be covariant to void:
internal delegate void VoidOut(); internal delegate object ObjectOut(); internal class Program { private static void Main() { VoidOut voidOut = () => { }; ObjectOut objectOut = () => new object(); // It looks like covariance is Ok here. voidOut = objectOut; // Because when we invoke [void voidOut()], we are invoking [object objectOut()].
// The return value of [object objectOut()] can be just ignored. voidOut(); } }
There are also some people asking about the parameter:
internal delegate void NoParameterIn(); internal delegate void ObjectIn(object @object); internal class Program { private static void Main() { NoParameterIn noParameterIn = () => { }; ObjectIn objectIn = (@object) => { }; // It looks like contravariance is Ok here. objectIn = noParameterIn; // Because when we invoke [void objectIn(object)], we are invoking [void noParameterIn()]. // The parameter of [void objectIn(object)] can be just ignored. objectIn(new object()); } }
Both of the above variance code cannot be compiled in C# 2.0 / 3.0 / 4.0. The reason is, on the virtual machine described by CLI, function with return value and function without return value work differently. If the variant is allowed, according to Eric Lippert:
we would be allowing you to misalign the IL stack!