Variance in Generic Interfaces (C# and Visual Basic)
Variance support enables a wider range of implicit conversions from one generic interface type to a related generic interface type or from a type that implements one generic interface to a type that implements a related generic interface. The following interfaces are variant:
IEnumerable (T is covariant)
IEnumerator (T is covariant)
IQueryable (T is covariant)
IGrouping (TKey and TElement are covariant)
IComparer (T is contravariant)
IEqualityComparer (T is contravariant)
IComparable (T is contravariant)
By using covariance, you can enable a generic interface that has type parameter A to be converted to the same interface with type parameter B if an implicit reference conversion exists from A to B. To illustrate the covariance feature, consider the generic interfaces IEnumerable<Object> and IEnumerable<String> (IEnumerable(Of Object) and IEnumerable(Of String) in Visual Basic). Because the String type is derived from Object, you might expect to be able to use a sequence of strings in place of a sequence of objects. However, without covariance, IEnumerable<String> isn't implicitly convertible to IEnumerable<Object>.
In earlier versions of the .NET Framework, the following code causes a compilation error in C# and in Visual Basic when Option Strict is on. However, now that the IEnumerable interface is covariant, you can use the strings variable in place of the objects variable, as the example shows.
Dim strings As IEnumerable(Of String) = New List(Of String)
Dim objects As IEnumerable(Of Object) = strings
IEnumerable<String> strings = new List<String>();
IEnumerable<Object> objects = strings;
Contravariance is like covariance except that it extends convertibility in the opposite direction. For example, IEqualityComparer<Object> is implicitly convertible to IEqualityComparer<String>. To illustrate contravariance, assume that you've created a BaseComparer class to compare instances of the BaseClass class. The BaseComparer class implements the IEqualityComparer<BaseClass> interface. Because the IEqualityComparer interface is contravariant, you can use BaseComparer to compare instances of classes that inherit from the BaseClass class, as the following code example shows.
' Simple hierarchy of classes.
Class BaseClass
End Class
Class DerivedClass
Inherits BaseClass
End Class
' Comparer class.
Class BaseComparer
Implements IEqualityComparer(Of BaseClass)
Public Function Equals1(ByVal x As BaseClass,
ByVal y As BaseClass) As Boolean _
Implements IEqualityComparer(Of BaseClass).Equals
Return (x.Equals(y))
End Function
Public Function GetHashCode1(ByVal obj As BaseClass) As Integer _
Implements IEqualityComparer(Of BaseClass).GetHashCode
Return obj.GetHashCode
End Function
End Class
Sub Test()
Dim baseComparer As IEqualityComparer(Of BaseClass) = New BaseComparer
' Implicit conversion of IEqualityComparer(Of BaseClass) to
' IEqualityComparer(Of DerivedClass).
Dim childComparer As IEqualityComparer(Of DerivedClass) = baseComparer
End Sub
// Simple hierarchy of classes.
class BaseClass { }
class DerivedClass : BaseClass { }
// Comparer class.
class BaseComparer : IEqualityComparer<BaseClass>
{
public int GetHashCode(BaseClass baseInstance)
{
return baseInstance.GetHashCode();
}
public bool Equals(BaseClass x, BaseClass y)
{
return x == y;
}
}
class Program
{
static void Test()
{
IEqualityComparer<BaseClass> baseComparer = new BaseComparer();
// Implicit conversion of IEqualityComparer<BaseClass> to
// IEqualityComparer<DerivedClass>.
IEqualityComparer<DerivedClass> childComparer = baseComparer;
}
}
For more examples, see Using Variance in Interfaces for Generic Collections (C# and Visual Basic).
Variance in generic interfaces is supported only for type arguments that are reference types. Value types don't support variance. For example, IEnumerable<int> can't be implicitly converted to IEnumerable<object> because integers are represented by a value type.
Dim integers As IEnumerable(Of Integer) = New List(Of Integer)
' The following statement generates a compiler error
' with Option Strict On, because Integer is a value type.
' Dim objects As IEnumerable(Of Object) = integers
IEnumerable<int> integers = new List<int>();
// The following statement generates a compiler errror,
// because int is a value type.
// IEnumerable<Object> objects = integers;
It's important to remember that generic classes and structs that implement variant interfaces are still invariant. For example, List implements the covariant interface IEnumerable. You can convert List<String> to IEnumerable<Object>, but you can't convert List<String> to List<Object>. The following code example illustrates this point.
' The following statement generates a compiler error
' because classes are invariant.
' Dim list As List(Of Object) = New List(Of String)
' You can use the interface object instead.
Dim listObjects As IEnumerable(Of Object) = New List(Of String)
// The following line generates a compiler error
// because classes are invariant.
// List<Object> list = new List<String>();
// You can use the interface object instead.
IEnumerable<Object> listObjects = new List<String>();
See Also
Reference
Using Variance in Interfaces for Generic Collections (C# and Visual Basic)