I'm not clear on the concept of Covariance in C# when it comes to interfaces. Strictly based on my example below, is this an example of Covariance, please describe why or why not.
class Program
{
static void Main()
{
ICarInterface car = new Car();
}
}
interface ICarInterface
{
void Start();
}
class Car : ICarInterface
{
public void Start()
{
}
}
In C#, covariance and contravariance enable implicit reference conversion for array types, delegate types, and generic type arguments. Covariance preserves assignment compatibility and contravariance reverses it.
Covariance is an indicator of the extent to which 2 random variables are dependent on each other. A higher number denotes higher dependency. Correlation is a statistical measure that indicates how strongly two variables are related. Values. The value of covariance lies in the range of -∞ and +∞.
The covariance matrix is denoted as the uppercase Greek letter Sigma.
Covariance is related to the interplay of subtyping and generics. As your program does not involve generics, it is not an example of covariance.
If U
is a subtype of V
(e.g. U = Pear
and V = Fruit
), then a generic type G<T>
is said to be covariant in T
if G<U>
is a subtype of G<V>
. For example, IEnumerable<Pear>
is a subtype of IEnumerable<Fruit>
: something that you can take pears from can be used to take fruit from.
If G
reverses the subtyping relation (G<V>
is a subtype of G<U>
), it is said to be contravariant in T
. For instance, Action<Fruit>
is a subtype of Action<Pear>
: something that you can put fruit in can be used to put pears in.
In your example, neither ICarInterface
nor Car
has a type parameter that it could be covariant in.
Specifically, in C#, a generic type is covariant in a type parameter T
if T
is marked with out
. It is contravariant in T
if T
is marked with in
.
No, this is not really covariance. This is just the implementation of an interface. Specifically, it is an example of assignment compatibility. Because the Car
class implements the ICarInterface
interface, a Car
object can be assigned to a variable of type ICarInterface
. An object of a more-specific type (Car
) was assigned to a storage area for a less-specific type (ICarInterface
), which works because the two types are compatible for the purposes of assignment.
Covariance is a slightly different thing. A type relationship is covariant if it preserves the ordering of types (from the more-specific to the more-generic). For example, IEnumerable<T>
is covariant with respect to type T
, so it preserves the ordering of types IEnumerable<Vehicle>
(more-generic) and IEnumerable<Car>
(more-specific). (In this example, we are of course assuming that Car
is a subclass of Vehicle
).
Eric Lippert has written an excellent article that distinguishes between covariance and assignment-compatibility. It gets slightly technical and theoretical, but you should definitely read it. I would not do it justice by trying to summarize it any further here.
An easier-to-understand example of covariance (at least in my opinion) is return-type covariance. This is where a derived class's override of a base-class method returns a more specific type. For example:
abstract class Habitat
{
public abstract Animal ApexPredator();
}
class Savanna : Habitat
{
public override Lion ApexPredator()
{ ... }
}
class Ocean : Habitat
{
public override Shark ApexPredator()
{ ... }
}
In this example, the abstract class Habitat
has two concrete subclasses: Savanna
and Ocean
. All habitats have an ApexPredator
, which is of type Animal
. But in a Savanna
object, the apex predator is a Lion
, whereas in an Ocean
object, the apex predator is a Shark
. This is legal and safe because Lion
and Shark
are both types of Animal
s.
Unfortunately, C# does not support return type covariance. It is, however, supported by C++ (including C++/CLI), Java, and a number of other object-oriented languages.
Here are some more concrete examples of covariance.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With