Covariância e Contravariância em C#, Parte Dois: Covariância de Array

1/3/2008 12:52:00 PM By Felipe Pessoto

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.

Covariância e Contravariância em C#, Parte Um

12/30/2007 11:09:00 PM By Felipe Pessoto

Vou escrever sobre uma série de artigos do Eric Lippert sobre Covariância e Contravariância. Pretendo dividir os artigos em partes como foi feito no original, pra deixar as coisas mais organizadas e fazer mudanças que forem necessárias pra um melhor entendimento.

Nesta primeira parte vamos entender alguns conceitos sobre tipos.

Primeiramente temos que entender que para dois tipos X e Y, pelo menos uma das seguintes afirmações é verdadeira:
X é maior do que Y.
X é menor do que Y.
X é igual a Y.
X não tem nenhuma relação com Y.

Considere uma hierarquia constituída de: Animal, Mamífero, Reptil, Girafa, Tigre, Cobra e Tartaruga, com seus relacionamentos (Mamífero é uma subclasse de Animal, etc). Mamífero é maior do que Girafa, menor que Animal, e, evidentemente, é igual à Mamífero. Mas Mamífero não é nem maior, nem menor, nem igual a Reptil, é apenas diferente.

Imagine que você tenha uma variável, toda variável tem um tipo que lhe é associado. Em runtime você pode armazenar um objeto que é uma instância de um tipo igual ou menor, ou seja, uma variável do tipo Mamífero pode ter uma instância de Girafa armazenados nela, mas não uma Tartaruga.

Esta idéia de armazenamento de um objeto em um local tipado é um exemplo específico de um princípio mais geral chamado de "princípio da substituição". Isto é, em muitos contextos, podemos substituir uma instância de um tipo "menor" por um "maior".

Agora podemos falar de variância. Considere uma "operação", que manipula tipos. Se o resultado da operação aplicada a qualquer X e Y e sempre resulta em dois tipos X' e Y' com o mesmo relacionamento X e Y a operação é considerada covariante. Se a operação inverte a "grandeza" e a "pequeneza" sobre os seus resultados, mas mantém a igualdade e independência, a operação é considerada "contravariante".

Isso é totalmente imaginário e provavelmente não muito claro. Na próxima parte vamos analisar como o C# implementa variância.

LINQ to Object

12/12/2007 12:54:00 PM By Felipe Pessoto

Agora com o Visual Studio 2008 estou fazendo alguns testes com os novos recursos do Framework 3.0 e 3.5.

Vou colocar aqui um exemplo de página que mostra os números pares, usando o LINQ e como fonte de dados um array.

Crie um novo WebSite, sem esquecer de usar o Framework 3.5:

Adicione um Button e um Label:

De dois cliques no Button para gerar o método do evento Click. Dentro do método insira o seguinte código(usei comentários para explicar o código):

 

//Nossa fonte de dados
int[] numeros = { 1, 2, 3, 4, 5, 6 };

//Query do Linq, depois da keyword in, especificamos a fonte de dados
//Usamos where para filtrar os resultados
//retornando só os que tiverem resto da divisão por 2 igual à 0
//o select serve para o Linq saber o que deve retornar
//daqueles que passarem pelo filtro
//no caso o proprio numero, mas poderiamos
//por exemplo retornar num.ToString()
var query = from num in numeros
where num % 2 == 0
select num;

//Passa cada numero retornado pro Label
//Perceba que a variavel query é definida como IEnumerable<int>
//var é um novo recurso do Framework 3.5
//O tipo da variável é definido na compilação
//A variavel continua sendo tipada
foreach (var numero in query)
{
lblResultado.Text += numero.ToString() + "<br>";
}

Agora tecle F5 para abrir o website e clique no Button, vai aparecer os números 2, 4 e 6:

Usando CopySourceAsHtml no Visual Studio 2008

12/10/2007 2:35:00 AM By Felipe Pessoto

Esta ferramente é muito interessante pra quem costuma inserir trechos de códigos no blog ou site.

Infelizmente o instalador dele só está preparado para o VS2005, mas você pode usá-lo no VS2008.

Para isso faça o download desse arquivo zip.

Extraia os 3 arquivos na pasta C:\Documents and Settings\<USUARIO>\Meus documentos\Visual Studio 2008\Addins
Talvez a pasta Addins não exista, então você deve cria-la.

Depois de extraidos ele já está pronto pra usar. Só para confirmar, clique em Tools -> Add-in Manager e verifique se o CopySourceAsHtml está marcado.

Agora é só selecionar o trecho desejado e clicar em Copy As Html...

E o resultado é muito bom:

 

 


   19 NumberFormatInfo NumberInfo = new NumberFormatInfo();


   20 NumberInfo.CurrencySymbol = "@";


   21 NumberInfo.CurrencyDecimalDigits = 4;


   22 DemoBuilder.NumberFormat = NumberInfo;

 

Aqui no blog não vai sair muito bem por causa do pequeno espaço horizontal, mas o html gerado fica com o visual perfeitamente igual Visual Studio.

Criando um assembly em tempo de execução

12/4/2007 1:00:00 PM By Felipe Pessoto

Vou explicar os passos básicos de como criar um pequeno assembly em tempo de execução, usando Reflection. Usando esses recursos seria possível criar um compilador para IL.

Os assemblies são feitos de: Assembly Metadata, Type Metadata, Code (IL) e Resources

  • O Assembly Metadata define propriedades como Nome e Versão do assembly
  • Type Metadata define todas as descrições de um tipo, como métodos, propriedades, nome de classe, etc
  • Code(IL) é o próprio código
  • Resources são imagens, strings, arquivos, etc

Cada assembly deve conter pelo menos um Modulo. Os módulos são uma espécie de caixa que guarda as informações sobre os Tipos.

Então vamos seguir a seguinte ordem, criamos um assembly, um modulo e um tipo, e já teremos o básico.

 

//namespaces necessarios
using System.Reflection;
using System.Reflection.Emit;

//Criamos um AssemblyName
AssemblyName AsyNome = new AssemblyName("MeuAssembly");
//Então definimos um novo assembly no atual AppDomain, não é possível criar um assembly a partir do próprio construtor
AssemblyBuilder AsyBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(AsyNome, AssemblyBuilderAccess.RunAndSave);
//AssemblyBuilderAccess.RunAndSave quer dizer que podemos tanto executar esse assembly, quanto salva-lo

//Criamos o modulo a partir do assembly
ModuleBuilder ModBuilder = AsyBuilder.DefineDynamicModule("MeuModulo", "modulo.dll");
//E um tipo a partir do modulo. Este tipo é publico e serializável, você pode atribuir várias propriedades com o operador |
TypeBuilder TipoBuilder = ModBuilder.DefineType("Meutipo", TypeAttributes.Public | TypeAttributes.Serializable);

//Agora podemos definir os membros do tipo, como construtores, métodos, propriedades, etc
//Vamos criar um campo
FieldBuilder CampoBuilder = TipoBuilder.DefineField("Campo", typeof(int), FieldAttributes.Public);

//Podemos tambem salvar o assembly para uso posterior
AsyBuilder.Save("modulo.dll");‚Äč
 

Neste exemplo foi usado somente o FieldBuilder, mas você pode criar qualquer outro membro usando os: ConstructorBuilder para construtor, EnumBuilder para enum, EventBuilder para eventos, etc

Você pode usar esse recurso para criar um software que aceite plug-ins por exemplo.