Covariância e Contravariância em C#, Parte Dois: Covariância de Array
C# implementa variância de duas maneiras. Hoje apresentarei a maneira incorreta.
Desde o C# 1.0, arrays onde o tipo do elemento é um tipo por referência são covariantes. Isto é perfeitamente correto:
Animal[] animais = new Girafa[10];
Desde que Girafa seja menor que Animal(isto é, Girafa herda de Animal), fazer um array dela é uma operação sobre tipos covariantes. Girafa[] é menor que Animal[], então sua instância se enquadra na variável.
Infelizmente, este tipo particular de covariância não é totalmente correto. Foi acrescentada à CLR porque o Java a implementa e os designers da CLR queriam uma linguagem parecida com o Java. Então foi adicionado ao C#, porque o recurso estava disponível na CLR. Esta decisão foi muito controversa no desenvolvimento, mas não há nada que possamos fazer em relação a isso agora.
Porque está incorreto? Porque deve ser sempre permitido colocar Tartaruga em um array de Animal. Com a covariância da array na linguagem e na runtime você não pode garantir que uma array de Animal pode aceitar uma Tartaruga porque por trás dela pode ser um array de Girafa.
Isto significa que tornamos um erro que podia ser pego pelo compilador em um que só pode ser pego no momento da execução. Isso também significa que toda vez que você colocar um objeto em um array, temos de fazer uma verificação em tempo de execução para garantir que o tipo funciona ou se gera uma exceção. Isso é potencialmente caro se você está colocando muitas coisas no array.
Um exemplo que você pode testar e verificar que realmente passa pelo compilador, mas gera uma exceção em tempo de execução:
class Animal { }
class Girafa : Animal { }
class Tartaruga : Animal { }
Animal[] animais = new Girafa[10];
animais[0] = new Tartaruga();
Na próxima parte vamos discutir uma espécie de variância que foi adicionado ao C# 2.0, que não tem problemas como esse.