Co and contra-variance were introduced to VB.NET and C# to make working with certain classes more natural (“because it should work”). But beware, I was experimenting a bit with this and found following possible breaking change. I started with these two classes:
C#
1: public class Person
2: {
3: // ...
4: }
5:
6: public class Vip : Person
7: {
8: // ...
9: }
VB.NET
1: Public Class Person
2: '
3: End Class
4:
5: Public Class Vip
6: Inherits Person
7: '
8: End Class
Then I added a collection of people:
C#
1: public class PersonList
2: {
3: public void Add(object obj) {
4: // ...
5: }
6:
7: public void Add(Person p)
8: {
9: // ...
10: }
11:
12: public void Add(IEnumerable<Person> people)
13: {
14: foreach (Person p in people)
15: {
16: // ...
17: }
18: }
19: }
VB.NET
1: Public Class PersonList
2: Public Sub Add(ByVal obj As Object)
3: '
4: End Sub
5:
6: Public Sub Add(ByVal person As Person)
7: '
8: End Sub
9:
10: Public Sub Add(ByVal list As IEnumerable(Of Person))
11: '
12: End Sub
13: End Class
Next I create a PersonList collection and then add a list of Vip’s:
C#
1: class Program
2: {
3: static void Main(string[] args)
4: {
5: PersonList people = new PersonList();
6:
7: List<Vip> others = new List<Vip> {
8: new Vip(), new Vip()
9: };
10:
11: people.Add(others);
12: }
13: }
VB.NET
1: Sub Main()
2: Dim people As New PersonList
3: Dim others As New List(Of Vip)(New Vip() {New Vip(), New Vip()})
4: people.Add(others)
5: End Sub
When I compile this in Visual Studio 2008 the first Add method, taking the object argument, gets called.
But with Visual Studio 2010 (using the .NET 4 target) the third Add method gets called. This is because now IEnumerable<T> is co-variant and will now convert the List<Vip> to an IEnumerable<Person>.
Ok, granted. This is NOT something you will see every day, but if you ever encounter it, this might be very confusing.
This kind of problem can also occur with extension methods and user-defined conversions…