Skip to main content

A evolução do C# - O início

Hoje vamos falar sobre História. Não aquela História das Guerras Mundiais, mas a história da evolução do C# como uma linguagem. Tenho em mente analisar as partes principais de uma das linguagens de programação em massa mais utilizadas no mundo. A grande parte do que vou falar é fruto de muita pesquisa, em parte porque quando o C# e o .NET foram disponibilizados eu ainda não tinha escrito uma única linha de código, por isso podemos dizer que sou novato neste mundo.

No entanto, estou a esforçar-me por mudar isso, e cada nova descoberta é feita com muito entusiasmo da minha parte, e eu considero-me extremamente pro-activo, o que poderá até irritar quem está à minha volta. Uma das razões pelas quais estou a escrever este post, e já agora todo este blog, é porque eu tenho gosto em aprender coisas novas e principalmente partilhá-las com outros. Chega de conversa, vamos ao que interessa!

O início do .NET

A plataforma .NET traz uma nova forma de programar as nossas aplicações. A Microsoft criou o termo "Managed Code" para se referir a código que corre no seu runtime chamado CLR (Common Language Runtime). O CLR é responsável por interpretar o que o nosso compilador de C# (ou VB, ou outra linguagem .NET) emite na forma de IL (Intermediate Language - IL). Este esquema foi retirado da Wikipedia e mostra-nos como como isto funciona.


Basicamente, o código que escrevemos é compilado para IL pelo compilador de C# e depois, quando corremos o programa, o JIT Compiler traduz essa IL para código nativo entendido pelo processador. Isto acontece enquanto corremos o nosso programa e tem o seu custo, como Eric Lippert explica neste artigo. 

Isto é tudo muito bonito, mas quais as vantagens reais do Managed Code?

IL
Para começar, a IL é uma vantagem em si. Compilar para uma linguagem comum significa que uma nova linguagem poderá ser adicionada à família .NET mais facilmente. IL representa uma abstracção no sentido em que tudo o que teríamos de fazer seria fazer o novo compilador emitir a IL correcta. Claro que isto é um exemplo extremamente simplista, mas a Microsoft já provou que é verdade criando uma família numerosa de linguagens de programação para a família .NET.

Para uma lista detalhada de linguagens que suportam este conceito de emitir IL, vejam este artigo. O número e a natureza variada de linguagens (desde linguagens Orientadas a Objectos até Funcionais) prova o quão versátil o conceito realmente é. 

Segurança
Penso que neste aspecto a coisa mais importante a notar é que, sendo uma plataforma, o CLR sabe o que está a correr nele. Existem mecanismos de segurança no CLR para garantir que o código é seguro e age como um supervisor. São coleccionados meta-dados associados com os assemblies carregados chamados Evidences. Neles, são contidas informações sobre o assembly, tal como quem publicou, a directoria da aplicação, etc. Tudo isto é analisado e são dados acessos ao código. Esta é uma explicação muito superficial, mas eu não sei nenhum dos detalhes intrínsecos neste assunto,  que poderão consultar aqui.

Base Class Library
Esta é a parte interessante. A BCL representa todas aquelas classes e estruturas básicas que são a base da .NET.Framework. Eu digo .NET porque a BCL é partilhada entre a família .NET. Isto significa que as mesmas classes, métodos, etc disponíveis no C# estão também disponíveis no VB. O objectivo principal desta abordagem é fazer com que o nosso trabalho seja centralizado enquanto programadores e dar-nos consistência dentro da plataforma. Assim, conseguimos encontrar uma abordagem unificada para interagir com partes mais baixas do sistema tal como escrever para Streams, interagir com o OS, e ter todas as classes básicas juntamente como suporte para muitas das features de toda a .NET Framework. Está também padronizada de acordo com os padrões ECMA e disponível para outras implementações, tal como o Mono.
    
Gestão de memória (Garbage Collector).
Esta é outra vantagem sem a qual eu nunca vivi, portanto não sei o que dizer quando me perguntarem como era o mundo antes. Mas vou tentar. Quando criamos um objecto, esse objecto vive na memória do nosso computador. Esse objecto está agora ao alcance do nosso código, e enquanto assim for ele não será destruido pelo Garbage Collector (GC). 

Apenas consigo imaginar como seria a vida antes de haver GC. Imagino que seja algo complicado, gerir a memória ocupada. Imaginemos que tínhamos ter em conta cada variável e preocupar-nos em dizer ao sistema que já não o estávamos a utilizar e que a memória podia ser reutilizada. Tenho a certeza que era uma tarefa complexa, aborrecida e também muito, muito propensa a erros. Consigo também imaginar que um grande número de aplicações sofra de memory leaks sem o saber, acabando por descobrir da pior forma.

Então, uma das ferramentas que o GC utiliza para limpar a memória é o conceito de scope. Scope indica se um determinado objecto está ou não acessível para o nosso código e pode ser destruido. Não pensem que um objecto é destruído assim que este sai de scope. Na realidade, o GC tem a sua própria forma de actuar, e não é possível determinar facilmente quando um ciclo de destruição vai acontecer. As razões são muitas: pode ser porque a memória está sob pressão, porque a Geração 0 do GC está esgotada...

Em relação a Gerações, quando o GC corre, ele começa por procurar estrategicamente em objectos base e depois recursivamente marca cada referência desses objectos como dentro de scope. Quando um objecto não está no scope, é marcado para destruição e o seu método Finalize é chamado. Após isso, a memória é libertada, mas os objectos que não foram destruidos são promovidos para a próxima Geração. Temos a Geração 0, 1 e 2. Na Geração 2 é onde estão os objectos que sobreviveram a duas passagens do GC e é menos provável que venham a ser destruídos. Estamos maioritariamente a falar de objectos static, variáveis globais e etc.

Portanto, o GC existe para tornar a nossa vida mais fácil, por isso estamos gratos que ele exista. Não temos de nos preocupar em libertar memória, apenas com o meu programa. Como é óbvio, precisamos de nos preocupar em limpar referências não necessárias dos nossos objectos, mas esse é um trabalho bem mais fácil. Para matar este assunto, basta-me fazer notar que esta foi uma introdução muito, muito superficial e que poderão ler mais aqui.

Segurança
Quando falo de segurança aqui, estou basicamente a dizer que estamos seguros de todos os conceitos de baixo nível com que teriamos de lidar em C, tal como pointers, acesso directo à memória... Quando criamos um objecto no heap, é-nos dada uma referência abstracta para esse objecto na memória e nunca o endereço daquele bloco em específico. Exceptions são outro tipo de segurança que obtemos do managed code. Quando ocorre um erro, podemos reagir-lhe e ter toda a informação sobre ele na nossa IDE. Em unmanaged code é um pouco mais difícil lidar com isto, e por vezes o programa sai sem qualquer aviso do porquê.

Static typing é outra das grandes vantagens do .NET. O compilador faz inúmeras verificações e com isso poupa-nos bastante tempo ao impedir a execução do programa ao invés de simplesmente o correr com erros. São verificados os types das variáveis, a declaração dessas mesmas variáveis e o cumprimento de inúmeras regras da linguagem mesmo antes de completarmos a build.

Produtividade
Não poderia acabar o artigo sem falar neste ponto. Este é provavelmente um dos efeitos secundários mais importantes da introdução do .NET. As linguagens da família .NET são muito produtivas, fáceis de aprender e, claro, sustentadas por muitas libraries que dão vida ao mundo do .NET. Ao longo dos anos, tornou-se extremamente apreciado em todo o mundo e assistimos a uma explosão nas metodologias e ferramentas que muitas empresas estão a desenvolver. A tecnologia evoluiu ainda mais, e hoje em dia tornou-se uma das mais importantes plataformas de desenvolvimento. Em C#, por exemplo, temos Generics, LINQ, Lambdas, Async e mais para vir. Nos próximos artigos irei falar dessas evoluções.

Para lá disto, temos uma das IDE's mais poderosas, extensíveis e versáteis do mundo. Estou a falar, claro, do Visual Studio. Apesar de serem independentes e ser possível utilizar outras IDE's, não poderia deixar de parte a ferramenta de trabalho que evoluiu especificamente para nos dar todo o potencial da plataforma.

Desvantagens
Managed code trouxe também algumas desvantagens consigo, como muitas pessoas inteligentes fazem notar. Velocidade é uma das desvantagens mais debatidas. Claro que ter uma linguagem compilada directamente para assembler é bastante mais rápido do que ter uma máquina virtual a interpretar IL. Este é um factor significante a ter em conta ao desenvolver sistemas de baixo nível tais como sistemas operativos ou drivers de baixo nível. Para a maioria das aplicações, é irrelevante porque no fim o que acaba por interessar é que o utilizador final tenha uma experiência fluída..  

Outra desvantagem é que o desenvolvimento multi-plataforma não é directamente suportado pela Microsoft, mas existem outras implementações multi-plataforma da .NET Framework, tal como o Mono.

Nos próximos capítulos vamos ver código real e a evolução que foi feita do C# 1.0 para o C# 2.0. Serão separadas as mudanças mais importantes por post, para poder entrar em detalhe em cada uma.

Obrigado a todos por lerem!

Comments

Popular posts from this blog

The repository's repository

Ever since I started delving into architecture,  and specifically service oriented architecture, there has been one matter where opinions get divided. Let me state the problem first, and then take a look at both sides of the barricade. Given that your service layer needs to access persistent storage, how do you model that layer? It is almost common knowledge what to do here: use the Repository design pattern. So we look at the pattern and decide that it seems simple enough! Let's implement the shit out of it! Now, let's say that you will use an ORM - here comes trouble. Specifically we're using EF, but we could be talking about NHibernate or really any other. The real divisive theme is this question: should you be using the repository pattern at all when you use an ORM? I'll flat out say it: I don't think you should... except with good reason. So, sharpen your swords, pray to your gods and come with me to fight this war... or maybe stay in the couch?

The evolution of C# - Part III - C# 2.0 - Iterators

It's been a while since i wrote the last post, but i did not forget my purpose of creating a series that shows the evolution of C#. Today i came here to talk about one of the most useful features of C#, even if you dont know you're using it. Let's talk about iterators ! What is an iterator? For those of you who didn't read about the iterator pattern somewhere in the internet or in the "Gang of Four" book, you can read a description  here . The iterator is a class/object/whatever which knows how to traverse a structure. So, if you have a list or collection of objects, an iterator would have the knowledge of how to traverse that collection and access each element that it contains. The iterator is a well known design pattern and is behind many of the wonderful that we have nowadays in .NET (Linq comes to mind). Why is it a feature? Truth be told, an iterator is a concept well known way before .NET even existed. Being an OO Design Pattern, the iterator has

My simplest and most useful type

I have been doing some introspection on the way I write code to find ways that I need to improve. I consider this a task that one must do periodically so that we keep organized. There is a very, very simple problem that occurs in every application I know: How to return the results of an operation to the user? I've seen many implementations. Some return strings, some throw exceptions, some use out parameters, reuse the domain classes and have extra properties in there, etc. There is a myriad of ways of accomplishing this. This is the one I use. I don't like throwing exceptions. There are certainly cases where you have no choice, but I always avoid that. Throughout my architectures there is a single prevalent type that hasn't changed for years now, and I consider that a sign of stability. It is so simple, yet so useful everywhere. The name may shock you, take a look: Yes, this is it. Take a moment to compose yourself. Mind you, this is used everywhere , in every