Normalmente, quando eu vou dormir, também ponho o meu computador a dormir.
Mas, desde há alguns meses, o computador acordava sozinho e eu não conseguia percber porquê.
Com a ajuda do Pete consegui perceber porquê:
C:\>PowerCfg -LASTWAKE
Wake History Count - 1
Wake History [0]
Wake Source Count - 1
Wake Source [0]
Type: Wake Timer
Owner: [PROCESS] \Device\HarddiskVolume2\Windows\System32\services.exe
Owner Supplied Reason: Windows will execute '\Microsoft\Windows\Media Center\mcupdate_scheduled' scheduled task that requested waking the computer.
Usando PowerCfg –LASTWAKE é possível saber o que acordou o computador da última vez que este acordou.
Afinal era apenas uma tarefa agendada do Media Center que acordava o meu computador.
Depois de instalar o Visual Studio 2010, inadvertidamente acabei com o SQL Server 2008 Express instalado, tendo já instalado o SQL Server 2008 Developer. Apenasquando ia actualizar para o SQL Server 2008R2 descobri que tinha duas instancias instalads.
Procurei por todo o lado e não consegui encontrar nenhuma forma de remover a instância SQLEXPRESS.
Apenas quando decidi desinstalar o SQL Server por completo descobri como o fazer.
|
NoWindows 7, ir a Programs and Features e escolher Microsoft SQL Server 2008 R2 (64-bit) (ou qualquer que seja a versão de SQL Server de que se pretende remover a instância). |
 |
|
Escolher Remove (ou Add, se se pretende adicionar uma instância). |
 |
|
Seguir os passos e escolher a instância que se pretende remover. |
 |
Seleccionar as funcionalidades que se pretende remover (seleccionar todas as funcionalidades para remover a instância). Nota: Não remover as funcionalidades partilhadas se não se pretendem remover outras instâncias |
 |
Talvez seja algo que eu devesse saber, mas não sabia e não foi nada fácil encontrar.
Em alguns dos meus sistemas Windows 7, após a instalação do Windows Virtual PC, não tinham a opção Create virtual machine na pasta das Virtual Machines.
O Bob Comer indicou-me esta entrada no Virtual PC Guy's Blog mas, antes de tentar as receitas apresentadas (que envolviam reiniciar a sessão ou o sistema), tentei simplesmente usar a opção Reset Folders na aba View das Folder Options. E o problema foi imediatamente resolvido.
Portanto, se estiverem com o mesmo problema, talvez queiram tentar primeiro esta solução simples.
No final, à cautela, adicionei os seguites atalhos ao menu Iniciar:
-
Windows Virtual PC Wizard
%SystemRoot%\System32\VPCWizard.exe
-
Windows Virtual PC Settings
%SystemRoot%\System32\VPCSettings.exe
Desde o Visual Studio 2005, que o Visual Studio traz um servidor web de desenvolvimento: o ASP.NET Development Server.
Tenho usado, desde então, este servidor web para projectos de teste simples com o Visual Studio 2005 e o Visual Studio 2008 em Windows XP Professional no laptop do trabalho e em Windows XP Professional, Windows Vista 64bit Ultimate e Windows 7 64bit Ultimate no desktop de casa sem qualquer problema (além do conhecido problema com custom identities, quero dizer).
Quando recebi um novo laptop no trabalho, instalei o Windows Vista 64bit Enterprise e o Visual Studio 2008 e, para grande surpresa minha, oASP.NET Development Server não funcionava.
Comecei a procurar diferenças entre os dois sistemas e as mais notórias eram:
|
Sistema |
Laptop |
Desktop |
|
SKU |
Windows Vista 64bit Enterprise |
Windows Vista 64bit Ultimate |
|
Pertencente a um domínio |
SIM |
Não |
|
Anti-Vírus |
McAffe |
ESET |
Depois de me certificar que nenhuma política de domínio estava a ser aplicada ao laptop ou ao meu utilizador e de que nada estava a ser registado pelo anti-vírus, as minhas suspeitas penderam para o facto de o laptop estar uma correr uma SKU Enterprise e o desktop estar a correr uma SKU Ultimate. Depois de ter tido alguns problemas com outras aplicações, tinha a certeza de que o problema era a SKU Enterprise, mas nunca encontrei uma solução para o problema. Porque, na altura, não estava a desenvolver para web, deixei estar.
Depois de actualizar para o Windows 7, o problema persistiu. Mas, porque, na altura, não estava a desenvolver para web, deixei estar – outra vez.
Qgora que instalei o Visual Studio 2010, tinha de resolver o problema. Depois de pesquisar alguns forums e alguns blogues que, ou não ofereciam alguma solução, ou ofereciam soluções demaisado complicadas envolvendo, em alguns casos, mexidas no registry, cheguei à conclusão de que a solução até era muito simples.
Quando o Windows Vista é instalado, o ficheiro hosts, de acordo com isto, contem a seguinte definição:
127.0.0.1 localhost
::1 localhost
Isto não era o que eu tinha no meu ficheiro hosts do laptop. O que eu tinha era isto:
#127.0.0.1 localhost
#::1 localhost
Posso ter sido eu mesmo a modificá-lo, mas pelo número de pessoas que encontrei queixando-se do mesmo problema no Windows Vista, provavelmente, era isto mesmo que tinha sido lá deixado pela instalação.
A instalação do Windows 7 deixam o ficheiro hosts com isto:
#127.0.0.1 localhost
#::1 localhost
Apesar do ASP.NET Development Server funcionar correctamente no Windows 7 64bit Ultimate, no Windows 7 64bit Enterprise necessita que o ficheiro hosts tenha a seguinte definição:
127.0.0.1 localhost
::1 localhost
E suspeito que o mesmo seja necessário para o Windows Vista 64bit Enterprise.
Recentemente um amigo perguntou-me algo como: “Como é que obtenho as definições regionais de um pedido a um web server?”
Tanto quanto sei, o web browser apenas envia o header HTTP only Accept-Language e nada mais. Pode-se pegar nesta informação e usar as definições regionais por omissão, mas se o utilizador for como eu, estarão erradas.
E qual é o problema de não acertar com as definições regionais do cliente?
Se se estivermos apenas a gerar HTML e mantivermos a coerência, não há qualquer problema. Mas, e se o utilizador quiser copiar valores numéricos e/ou datas/horas para, por exemplo, um folha Excel? Ou se se quiser exportar os dados para um ficheiro em formato CSV?
Uma solução
Consultando a JScript Language Reference, descobri que tanto Number com Date têm métodos toString relacionados com locale e comecei a experimentá-los.
Definições de formatos numéricos
Para os formatos numéricos, primeiro necessitamos de um número que se comporte de forma previsíve em com qualquer cultura (“qualquer cultura” significa “qualquer cultura que eu conheça”) e todas as definições possíveis e convertê-lo para string usando o método toLocaleString:
var number = 111111111.111111111;
var numberString = number.toLocaleString();
(Com as minhas definições regionais, numberString terá o valor 111 111 111.11)
Para obter o separador decimal, temos de obter o primeiro carácter, a partir do fim, que não seja 1:
var decimalSeparator;
var decimalDigits;
for (var i = numberString.length - 1; i >= 0; i--) {
var char = numberString.charAt(i);
if (char != "1") {
decimalSeparator = char;
decimalDigits = numberString.length - i - 1;
break;
}
}
Se se contarem quantos 1s saltámos, obtme-se o número de casas décimais.
Do mesmo modo, o primeiro carácter, a contar do início, que não seja 1 é o separador de grupos:
var groupSeparator;
for (var i = 0; i < numberString.length; i++) {
var char = numberString.charAt(i);
if (char != "1") {
groupSeparator = char;
break;
}
}
Tendo obitod o separador de grupos, podemos obter a composição dos mesmos (podem não ter todos o mesmo número de dígitos):
var digitGrouping = numberString.substring(0, numberString.length - decimalDigits - 1).split(groupSeparator);
for (g in digitGrouping) {
digitGrouping[g] = digitGrouping[g].length;
}
Definições de data e hora
Os valores de data e hora são mais difíceis de interpretar e podem não ser necessárias todas as definições, por isso, vou-vos deixar este como exercício:
var dateTime = new Date(9999, 11, 31, 23, 30, 45);
dateTimeString = dateTime.toLocaleString();
Definições de listas
A última definição é a do separador de listas (muito útil para os tais ficheiros CSV):
var list = ["a", "b"];
listSeparator = list.toLocaleString().substring(1, 2);
Página de testes
Aqui está uma página de testes para testar todas estas definições:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>Test Page</title>
<style type="text/css">
label { width: 8em; text-align: right; padding-right: 0.5em; white-space: nowrap; }
span { border: 1px solid; white-space: nowrap; }
</style>
<script type="text/javascript">
function init() {
document.all.userLanguage.innerText = window.navigator.userLanguage;
document.all.systemLanguage.innerText = window.navigator.systemLanguage;
// Decimal separator and decimal digits
var number = 111111111.111111111;
var numberString = (111111111.111111111).toLocaleString();
var decimalSeparator;
var decimalDigits;
for (var i = numberString.length - 1; i >= 0; i--) {
var char = numberString.charAt(i);
if (char != "1") {
decimalSeparator = char;
decimalDigits = numberString.length - i - 1;
break;
}
}
document.all.decimalSeparator.innerText = decimalSeparator;
document.all.decimalDigits.innerText = decimalDigits;
// Digit grouping separator and digit goups
var groupSeparator;
for (var i = 0; i < numberString.length; i++) {
var char = numberString.charAt(i);
if (char != "1") {
groupSeparator = char;
break;
}
}
document.all.groupSeparator.innerText = groupSeparator;
var digitGrouping = numberString.substring(0, numberString.length - decimalDigits - 1).split(groupSeparator);
for (g in digitGrouping) {
digitGrouping[g] = digitGrouping[g].length;
}
document.all.digitGrouping.innerText = digitGrouping.toString();
// Date format
var dateTime = new Date(9999, 11, 31, 23, 30, 45);
var dateTimeString = dateTime.toLocaleString();
document.all.dateTimeFormat.innerText = dateTimeString;
// List separator
var list = ["a", "b"];
var listSeparator = list.toLocaleString().substring(1, 2);
document.all.listSeparator.innerText = listSeparator;
}
</script>
</head>
<body onload="init()">
<p><label for="userLanguage">User language:</label><span id="userLanguage"></span></p>
<p><label for="systemLanguage">System language:</label><span id="systemLanguage"></span></p>
<p><label for="decimalSeparator">Decimal separator:</label><span id="decimalSeparator"></span></p>
<p><label for="decimalDigits">Decimal digits:</label><span id="decimalDigits"></span></p>
<p><label for="groupSeparator">Digit separator:</label><span id="groupSeparator"></span></p>
<p><label for="digitGrouping">Digit grouping:</label><span id="digitGrouping"></span></p>
<p><label for="dateTimeFormat">Date/Time format:</label><span id="dateTimeFormat"></span></p>
<p><label for="listSeparator">List separator:</label><span id="listSeparator"></span></p>
</body>
</html>
Este livro, com vários exemplos práticos, apresenta as principais características relacionadas com a construção de aplicações Web através da nova plataforma da Microsoft, ASP.NET 4.0. Inicia com a apresentação da framework que serve de suporte ao desenvolvimento de páginas (Web forms e ASP.NET server controls simples) e introduz gradualmente todas as novas funcionalidades disponibilizadas.
Mais compacta do que as suas antecessoras (parte do conteúdo foi movido para o para o sítio da FCA na forma de apêndices), esta nova obra, em que foi dado ênfase às novas funcionalidades da recente versão 4.0, tem como objectivo ensinar o programador que se está a iniciar na plataforma ASP.NET, sendo também uma ferramenta indispensável para o programador conhecedor da framework ASP.NET que pretenda fazer a transição para a nova versão.
Desta vez há boas notícias para os leitures Brasileiros. O livro vai ser distribuído no Brasil por:
Zamboni Comércio de Livros Ltda.
Av.Parada Pinto, 1476
São Paulo – SP
Telf. / Fax: +55 11 2233-2333
E-mail: zambonibooks@terra.com.br
O nosso livro (LINQ Com C#) ainda não é distribuído por este distribuidor mas podem sempre contactá-lo para ver se é possível obtê-lo através dele.
Gostaria de agradecer a quem esteve presente na minha sessão no TechDays 2010 e espero que tenha conseguido passar a mensagem das novidades da última versão da linguagem C#.
Para quem não esteve na sessão (ou esteve e quiser rever o conteúdo), a apresentação pode ser descarregada daqui.
Os exemplos de código podem ser descarregados daqui.
Aqui fica também a lista dos recursos mencionados na apresentação:
- A Evolução Do C#
- Covariância e Contravariância
- Argumentos com Nome e Opcionais
- Programação Dinâmica
- Melhoramentos na Interoperabilidade com COM
- Conclusão
A resolução dinâmica e argumentos com nome e opcionais melhoram largamente a experiência de interoperar com APIs COM como as Office Automation Primary Interop Assemblies (PIAs). Mas, para aliviar ainda mais o desenvolvimento de interoperabilidade com COM, foram adicionados algumas funcionalidades específicas para COM ao C# 4.0.
Omissão do Modificador ref
Devido ao diferente modelo de programação, muitas APIs COM contêm muitos parâmetros por referência. A intenção deste parâmetros não é modificar os argumentos que lhes são passados, mas apenas outro modo de passar valores.
Especificamente com métodos COM, o compilador permite declarar a chamada ao método passando os argumentos por valor e gerará automaticamente variávias temporárias para manter os valores por forma a poder passá-los por referência e descartará os seus valores após o retorno da chamad ao método. Do ponto de vista do porgramador, os argumentos são passados por valor.
Sendo assim, esta chamada:
object fileName = "Test.docx";
object missing = Missing.Value;
document.SaveAs(ref fileName,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing,
ref missing, ref missing, ref missing);
pode ser escrita assim:
document.SaveAs("Test.docx",
Missing.Value, Missing.Value, Missing.Value,
Missing.Value, Missing.Value, Missing.Value,
Missing.Value, Missing.Value, Missing.Value,
Missing.Value, Missing.Value, Missing.Value,
Missing.Value, Missing.Value, Missing.Value);
E porque todos os parâmteros que recebem o valor Missing.Value têm esse valor como valor por omissão, a declaração da chamada ao método pode ser reduzida a:
document.SaveAs("Test.docx");
Importação Dinâmica
Muitos métodos COM aceitam e retornam tipos variant, que são representados nas PIAs como object. Na grande maioria dos casos, o programador que está a chamar estes métodos conheço o tipo estático dos objectos que estão a ser retornados pelo contexto da chamada, mas ainda são obrigados a explicitamente converter o valor retornado para fazer uso desse seu conhecimento. Estas conversões são tão comuns que se tornam um incómodo.
Para tornar a vida do programador mais fácil, é agora possível importar as APIs COM de modo a que os variants seja representados usando o tipo dynamic o que quer dizer que as assinaturas COM têm agora ocorrências de dynamic em vez de object.
Isto quer dizer que membros de um objecto retornado podem agora ser facilemtne acedidos ou atribuídos a variáveis fortemente tipadas sem necessiade de conversão explícita.
Em vez deste código:
((Excel.Range)(excel.Cells[1, 1])).Value2 = "Hello World!";
pode ser usado este:
excel.Cells[1, 1] = "Hello World!";
E em vez deste:
Excel.Range range = (Excel.Range)(excel.Cells[1, 1]);
pode ser usado este:
Excel.Range range = excel.Cells[1, 1];
Propriedades Indexadas E Por Omissão
Algumas funcionalidades das interfaces COM ainda não estão disponíveis em C#. No topo desta lista estão as propriedades indexadas e as propriedades por omissão. Como mencionado anteriormente, estas poderão ser usadas se a interface COM for acedida dinâmicamente, mas não serão reconhecidas por código C# estáticamente tipoado.
No PIAs – Equivalênica De Tipos E Tipos Embebidos
Para assemblies indentificadas com PrimaryInteropAssemblyAttribute, o compilador criará tipos equivalentes (interfaces, estruturas, enumerados e delegates) e embebê-los-á na assembly gerada.
Para reduzir a dimensão final da assembly gerada, apenas os tipos e seus membros usados serão gerados e embebidos.
Embora isto torne o desenvolvimento e distribuição de aplicações que usam os componentes COM mais fácil porque não há necessidade de distribuír as PIAs, quem desenvolve os componentes COM continua a ter de desenvolver as PIAs.
A principal funcionalidade do C# 4.0 é a programação dinâmica. Não apenas em termos de tipos dinâmicos, mas, un sentido mais lato, a capacidade de falar com qualquer coisa que não seja staicamente tipada para ser um objecto .NET.
Dynamic Language Runtime
O Ambiente de Execução para Linguagens Dinâmicas (Dynamic Language Runtime - DLR) é um pedaço de tecnologia que unifica a programação dinâmica na plataforma .NET, da mesma forma que o Ambiente de Execução Comum para Linguagens (Common Language Runtime - CLR) tem sido a plataforma comum para linguagens estaticamente tipadas.
A CLR sempre teve capacidedes dinâmicas. Sempre foi possível usar reflecção, mas o seu prinicpal objectivo nunca foi ser um ambiente de programação dinamido e faltava-lhe algumas funcionalidades. A DLR assenta na CLR e adiciona essas funcionalidades em falta à plataforma .NET.

A DLR é a infra-estrutura base que consiste em:
-
Dynamic Dispatch
As mesmas árvores de expressões usedadas no LINQ, agora melhoradas para suportar instruções.
-
Dynamic Dispatch
Despacha as invocações para o binder apropriado.
-
Call Site Caching
Para melhor eficiência.
As linguagens dinâmicas e as linguagens dinâmicas assentam na DLR. O IronPython e o IronRuby já assentavam na DLR, e agora, o suporte para usar a DLR foi adicionado ao C# e ao Visual Basic. Espera-se que outras linguagens construídas sobre a CLR passem também, no futuro, a usar a DLR.
Sob a DLR encontram-se os binders que falam com uma variedade de diferentes tecnologias:
-
.NET Binder
Permite falar com objectos .NET.
-
JavaScript Binder
Permite falar com JavaScript em SilverLight.
-
IronPython Binder
Permite falar com IronPython.
-
IronRuby Binder
Permite falar com IronRuby.
-
Permite falar com COM.
Com todos estes binders é possível ter uma única experiência de programação para falar com todos estes ambientes que não são objectos .NET estaticamente tipados.
O Tipo Estático dynamic
Tomemos este tradicional código estaticamente tipado:
Calculator calculator = GetCalculator();
int sum = calculator.Sum(10, 20);
Porque a variável que recebe o valor de retorno do método GetCalulator é estaticamente tipada para ser do tipo Calculator e, porque o tipo Calculator tem um método Add que recebe dois inteiros e retorna um inteiro, é possível chamar esse método Sum e atribuír o seu valor de retorno a uma variável etaticamente tipada como inteiro.
Agora suponhamos que a calculadora não era uma classe .NET estaticamente tipada, mas, em vez disso, um objecto COM ou algum códiogo .NET de que não conhecemos o tipo. De repente tornou-se muito penoso chamar o método Add:
object calculator = GetCalculator();
Type calculatorType = calculator.GetType();
object res = calculatorType.InvokeMember("Add", BindingFlags.InvokeMethod, null, calculator, new object[] { 10, 20 });
int sum = Convert.ToInt32(res);
E se a calculador for um objecto JavaScript?
ScriptObject calculator = GetCalculator();
object res = calculator.Invoke("Add", 10, 20);
int sum = Convert.ToInt32(res);
Para cada domínio dinâmico temos uma experiência de programação diferente o que torna a unificação do código muito difícil.
Com o C# 4.0 é agora possível escrever código como este:
dynamic calculator = GetCalculator();
int sum = calculator.Add(10, 20);
Basta declarar uma variável cujo tipo estático é dynamic. dynamic é uma pseudo palavra reservada (como var) que indica ao compilador que a resolução das operações sobre calculator será efectuada de forma dinâmica.
Deve-se encarar um dynamic como um object (System.Object) com semântica dinâmica associada. Qualquer coisa pode ser afectada a dynamic.
dynamic x = 1;
dynamic y = "Hello";
dynamic z = new List<int> { 1, 2, 3 };
Em tempo de execução, todos os objectos terão um tipo. No exemplo acima, No exemplo precedente, em tempo de execução, x é do tipo System.Int32.
Quando um ou mais operandos de uma operação são tipados dynamic, a selecção do método a chamar é deferida para tempo de execução ao invés de ser feita em tempo de compilação. Então o tipo em tempo de execução é substituído em todas as variáveis e a resolução de sobreposições (overloads) ocorre, tal como em tempo de com ocorreria em tempo de compilação.
O resultado de qualquer operação dinâmtica é sempre dynamic e, quando um objecto dynamic é atribuído a algo, ocorre uma conversão dinâmica.
| Código |
Resolução |
Método |
double x = 1.75;
double y = Math.Abs(x);
|
Tempo de compilação
|
double Abs(double x)
|
dynamic x = 1.75;
dynamic y = Math.Abs(x);
|
Tempo de execução
|
double Abs(double x)
|
dynamic x = 2;
dynamic y = Math.Abs(x);
|
Tempo de execução
|
int Abs(int x)
|
O código acima será sempre fortemente tipado. A diferença é que, no primeiro caso a resolução do método é feita m tempo de compilação, e nos outros em tempo de execução.
IDynamicMetaObjectObject
A está DLR pré-preparada para conhecer objectos .NET, ojectos COM mas qualquer linguagem dinâmica pode implementar os seus próprios objectos dinãmicos, assim como qualquer programador pode implementar os seus objectos dinâmicos em C# através da implementação da interface IDynamicMetaObjectProvider. Quando um objecto implementa IDynamicMetaObjectProvider, pode participar na resolição de como os métodos são chamados ou as propriedaes acedidas.
A .NET Framework já contem duas implementações de IDynamicMetaObjectProvider:
-
DynamicObject : IDynamicMetaObjectProvider
A classe DynamicObject permite definir que operações podem ser executadas em objectos dinâmicos e como serão executadas essas operações. Por exemplo, pode-se definir o que acontece quando se tenta ler ou escreve uma propriedade, chamar um método ou efectuar operações matemática como adições e multiplicações.
-
ExpandoObject : IDynamicMetaObjectProvider
A classe ExpandoObject permite adicionar e remover membros às suas instâncias em tempo de execução e também obter os valores desses membros. Esta classe suporta a ligação dinâmica que permite usar um sintaxe normal como sampleObject.sampleMember, em vez de uma sintaxe mais complexa como sampleObject.GetAttribute("sampleMember").
Tal como mencionei na minha última entrada, expor publicamente métodos com argumentos opcionais é uma má prática (é por isso que o C# resitiu a tê-los, até agora).
Podem argumentar que o vosso método ou construtor tem demasiadas variações e ter mais de dez variantes é um pesadelo de manutenção, e têm razão. Mas a solução já existe há muito tempo: classes de argumentos.
O padrão da classe de argumentos é usado na .NET Framework por várias classes, com XmlReader e XmlWriter que usa este padrão nos seus métodos Create, desde a versão 2.0:
XmlReaderSettings settings = new XmlReaderSettings();
settings.ValidationType = ValidationType.Auto;
XmlReader.Create("file.xml", settings);
Com este padrão, não é necessário manter uma longa lista de sobreposições e quaisquer valores por omissão para propriedades de XmlReaderSettings (ou XmlWriterSettings para XmlWriter.Create) pode ser modificados ou até mesmo novas propriedades podem ser acrescentadas em futuras implementações sem que se quebre código já compilado existente.
Podem agora argumentar que é demasiado código para escrever, mas, com os inicializadores de objectos introduzidos no C# 3.0, o mesmo código pode ser escrito assim:
XmlReader.Create("file.xml", new XmlReaderSettings { ValidationType = ValidationType.Auto });
Parece-se com argumentos com nome e opcionais, não parece? E, quem sabe, numa futura versão do C#, pode até parecer-se com isto:
XmlReader.Create("file.xml", new { ValidationType = ValidationType.Auto });
Como parte do esforço de co-evolução do C# e do Visual Basic, o C# 4.0 introduz Argumentos com Nome e Opcionais.
Primeiro clarifiquemos o que são argumentos e o que são parâmetros:
De facto, a Especificação da Linguagem C# afirma em §7.5 que:
A lista de argumentos (§7.5.1) da invocação de um membro função providencia os valores que vão ser efectivamente usados como variáveis ou referências a variáveis como parâmetros do membro função.
Dadas as definições acima, podemos afirmar que:
-
Os parâmetros sempre tiveram, e continua a ter, nome.
-
Os parâmetros nunca foram, e continuam a não ser, opcionais.
Argumentos Com Nome
Até agora, a forma como o compilador de C# fazia corresponder os argumentos da declaração de uma chamada a um método era pela sua posição. O primeiro argumento providencia o valor para o primeiro parâmetro o segundo argumento providencia o valor para o segundo parâmetro, e por aí fora, independentemente do número de parâmetros. Se algum parâmetro não tivesse um argumento correspondente para lhe providenciar o valor, isso faria o compilador emitir um erro de compilação.
Para esta chamada:
Greeting("Mr.", "Morgado", 42);
este métdo:
public void Greeting(string title, string name, int age)
receberá como parâmetros:
-
title: “Mr.”
-
name: “Morgado”
-
age: 42
O que esta nova funcionalidade permite é usar os nomes dos parâmteros para identificar os argumentos correspondentes na forma: name:value
Nem todos os argumentos da lista precisam de ser identificados com o nome do correspondente parâmetros. No entanto, todos os argumentos com nome devem ser posicionados no fim da lista de argumentos. A correspondência entre os argumentos (e a avaliação do seu valor) e os parâmetros será feita primeiro pelo nome dos argumentos com nome (apesar destes se encontrarem no fim da lista) e em seguida pela posição, para os parâmteros para os quais ainda não foi atribuído valor.
Isto quer dizer que, para esta definição de método:
public void Method(int first, int second, int third)
esta declaração de chamada:
int i = 0;
Method(i, third: i++, second: ++i);
terá o seguinte código gerado pelo compilador:
int i = 0;
int CS$0$0000 = i++;
int CS$0$0001 = ++i;
Method(i, CS$0$0001, CS$0$0000);
o que dará aos parâmetros do método os seguintes valores:
- first: 2
- second: 2
- third: 0
Note-se que os nomes das variáveis geradas pelo compilador não são identificadores C# válidos. No entanto, são identificadores .NET válidos evitando, assim, colisões de nomes entre o código gerado pelo compilador e o código escrito pelo programador.
Para além de permitir reordenar a lista de argumentos, esta funcionalidade apresenta-se muito útil para documentar código que quando, por exemplo, a lista de argumentos é muito extensa ou é pouco claro o significado dos argumentos na declaração da chamada ao método.
Argumentos Opcionais
Agora os parâmetros pode ter valores por omissão:
public void Method(int first, int second = 2, int third = 3)
Os parâmetros com valores por omissão têm de ser os últimos da lista de parâmetros e esse valor é usado se o argumento correspondente estiver em falta na lista de argumentos da declaração da chamado ao método.
Para esta declaração de chamada:
int i = 0;
Method(i, third: ++i);
terá o seguinte código gerado pelo compilador:
int i = 0;
int CS$0$0000 = ++i;
Method(i, 2, CS$0$0000);
o que dará aos parâmetros do método os seguintes valores:
- first: 1
- second: 2
- third: 1
Porque, quando os parâmetros do método têm valores por omissão, os arumentos podem ser omitidos na declaração da chamada ao método, pode parecer-se com sobreposição de métodos, mas não é.
Embora métodos como este:
public StreamReader OpenTextFile(
string path,
Encoding encoding = null,
bool detectEncoding = true,
int bufferSize = 1024)
permitam que as sua chamadas sejam escritas assim:
OpenTextFile("foo.txt", Encoding.UTF8);
OpenTextFile("foo.txt", Encoding.UTF8, bufferSize: 4096);
OpenTextFile(
bufferSize: 4096,
path: "foo.txt",
detectEncoding: false);
O compilador trata os valores por omissão como trata os campos constantes tomando o seu valor e usando-o em vez de uma referência ao valor. Portanto, tal como com os campos constantes, os métodos com parâmetros com valores por omissão não devem ser expostos publicamente (não esquecer que membros internos podem ser aceiddos publicamente – InternalsVisibleToAttribute). Se tais métodos forem acedidos publicamente por outra assemby, esses valores serão codificados no código chamador e, se a assembly chamada tiver esses valores modificados, essa modificação não se reflectirá no código já compilado.
À primeira vista, pensei que usar os argumentos opcionais para chamar “mau” código era bom, mas a possibilidade de definir esse tipo de métodos era terrível. Mas depois apercebi-me que, uma vez que uso campos constantes, não será mau usar métodos com parâmetros com valores por omissão pra métodos de acesso privado.
Na minha última entrada, percorri o que é a variância em .NET 4.0 e C# 4.0 de uma form algo teórica.
Desta vez vou tentar ser mais terra a terra.
Dados:
class Base { }
class Derived : Base { }
Em que:
Trace.Assert(typeof(Base).IsClass && typeof(Derived).IsClass && typeof(Base).IsGreaterOrEqualTo(typeof(Derived)));
-
Covariância
interface ICovariantIn<out T> { }
Trace.Assert(typeof(ICovariantIn<Base>).IsGreaterOrEqualTo(typeof(ICovariantIn<Derived>)));
-
Contravâriancia
interface IContravariantIn<in T> { }
Trace.Assert(typeof(IContravariantIn<Derived>).IsGreaterOrEqualTo(typeof(IContravariantIn<Base>)));
-
Invariância
interface IInvariantIn<T> { }
Trace.Assert(!typeof(IInvariantIn<Base>).IsGreaterOrEqualTo(typeof(IInvariantIn<Derived>))
&& !typeof(IInvariantIn<Derived>).IsGreaterOrEqualTo(typeof(IInvariantIn<Base>)));
Onde:
public static class TypeExtensions
{
public static bool IsGreaterOrEqualTo(this Type self, Type other)
{
return self.IsAssignableFrom(other);
}
}
O C# 4.0 (e a .NET 4.0) introduziram covariância e contravariância em interfaces e delegates genericos. Mas afinal o que é a variância?
Segundo a Wikipedia, em álgebra multilinear, covariância e contravariância descrevem como a descrição quantitativa de certas entidades geométricas ou físicas variam quando passam de um systema de coordenadas para outro.(*)
Mas o que é que isto tem a ver com C# ou .NET?
Na teoria de tipos, um tipo T é maior (>) que o tipo S se S é um subtipo (deriva) de T, o que quer dizer que existe uma descrição quantitativa para tipos numa hierarquia de tipos.
Sendo assim, como é que a covariância e a contravariância se aplicam aos tipos genéricos do C# (e de .NET)?
Em C# (e em .NET), variância é uma relação entre uma definição de tipo genérico e um determinado tipo parâmetro de genéricos.
Dados dois tipos Base e Derivado, em que:
Uma definição de tipo genérico Genérico<T> é:
-
covariante em T se a ordem dos tipos construídos segue a ordem dos tipos parâmetros de genéricos : Genérico<Base> ≥ Genérico<Derivado>
-
contravariante em T se a ordem dos tipos construídos é inversa à ordem dos tipos parâmetros de genéricos : Genérico<Base> ≤ Genérico<Derivado>
-
invariante em T se nenhuma das regras acima se aplica.
Se aplicarmos a definição a arrays, podemos constatar que os arrays sempre foram covariantes porque este código é válido:
object[] objectArray = new string[] { "string 1", "string 2" };
objectArray[0] = "string 3";
objectArray[1] = new object();
Contudo, quando tentamos correr este código, a segunda afectação vai lançar uma ArrayTypeMismatchException. Apesar do compilador ter sido enganado para pensar que o código era válido porque um object está a ser atribuído a um elemento de um array de object, em tempo de execução, há sempre uma verificação de tipos para garantir que o tipo em tempo de execução da definição dos elementos do array é maior ou igual ao da instância quie está a ser atribuída ao elemento. No exemplo acima, porque o tipo em tempo de execução é array de string, a primeira afectação de elementos é válida porque string ≥ string e a segunda não é válida porque string ≤ object.
Isto leva-nos à conclusão de que, apesar dos arrays sempre terem sido covariantes, não são covariantes sem riscos – não é garantido que código que compila corra sem erros.
Em C#, a variância em relação um determinado tipo parâmetro de genéricos é forçada na declaração e não determinada pelo uso desse tipo parâmero de genéricos.
A covariância em relação a um determinado tipo parâmetro de genéricos é forçada através do modificador genérico out:
public interface IEnumerable<out T>
{
IEnumerator<T> GetEnumerator();
}
public interface IEnumerator<out T>
{
T Current { get; }
bool MoveNext();
}
Note-se o conveniente uso da palavra reservada out já existente. Além do benefício de não nos termos de lembrar de uma nova hipotética palavra reservada covariant, out (saída) é mais fácil de lembrar porque define que o tipo parâmetro de genéricos apenas pode ser usando em posições de saída — propriedades apenas de leitura e valores de saída de métodos.
Similarmente, a contravariância em relação a um determinado tipo parâmetro de genéricos é forçada através do modificador genérico in:
public interface IComparer<in T>
{
int Compare(T x, T y);
}
Mais uma vez, o uso da palavra reservada in (entrada) já existente é mais fácil de lembrar keyporque define que o tipo parâmetro de genéricos apenas pode ser usando em posições de entrada — propriedades apenas de escrita e parâmetros de métodos que não sejam por referência (ref) nem de saída (out).
Um tipo parâmetro de genéricos que não seja marcado covariante (out) ou contravariante (in) é invariante.
Porque a variância se aplica à relação entre uma definição de tipo genérico e um determinado tipo parâmetro de genéricos, uma definição de tipo genérico pode ser covariante, contravariante, invariante dependendo do tipo parâmetro genérico.
public delegate TResult Func<in T, out TResult>(T arg);
Na definição do delegate genérico acima, Func<T, TResult> é contravariant em T e convariante em TResult.
Todos os tipos da .NET Framework onde a variância podia ser aplicada foram alterados para que os seus tipos parâmetros de genéricos pudessem tirar partido desta funcionalidade.
Em resumo, as regras da variância em C# (e .NET) são:
-
A variância em relação a tipos parâmetros de genéricos está restringida aos tipos interfaces genéricas e delegates genéricos.
-
Uma interface genérica ou um delegate genérico podem ser, em simultâneo covariante, contravariante ou invariante em relação a diferentes tipos parâmetros de genéricos.
-
A variância aplica-se apenas a tipos referência: um IEnumerable<int> não é um IEnumerable<object>.
-
A variância não se aplica à combinação de delegates. Isto é, dados dois delegates do tipo Action<Derived> e Action<Base>, não é possível combinar o segundo delegate com o primeiro apesar de que o resulado ser seguro do ponto de vista do tipo. A variância permite que o segundo delegate seja atribuído a uma variável do tipo Action<Derived>, mas os delegates apenas podem ser combinados se os seus tipos forem uma correspondência exacta.
Quem desejar aprender mais acerca da variância em C# (e .NET), pode ler:
Nota: Por a variância ser uma funcionalidade da .NET 4.0 e não apenas do C# 4.0, estes conceitos também se aplicam ao Visual Basic 10.
A primeira versão do C# (C# 1.0) foi, essencialmente, o desenvolvimento de uma linguagem de programação para código gerido (managed code) que apelasse, pricipalmente, a programadores de C++ e Java.
Com a segunda versão (C# 2.0) pretendeu-se colmatar as falhas da primeria versão adicionado o que não tinha tido tempo de ser incluído. A principal funcionalidade desta versão foram os Genéricos.
Na terceira versão (C# 3.0) pretendeu-se reduzir a dissonância entre as linguagens de programação de uso geral e as base de dados. Para alcaçar este objectivo, foram adicionadas algumas funcionalidades da programação funcional e nascia o LINQ.
Novas tendências estão a aparecer na industria e as linguagens de programação modernas necessitam de ser mais:
-
Declarativas
Com as linguagens imperativas, mesmo sem deixar de ter em conta o quê, os programas têm de se focar no como. Isto leva à sobre-especificação da solução para o problema em mãos, tornando praticamente impossível ao motor de execução ser suficientemente esperto para optimizar a execução do programa e executá-lo mais eficientemente (parar o hardware disponível, por exemplo).
As linguagens declarativas, por outro lado, focam-se no quê e deixam o como ao motor de execução. O LINQ tornou o C# mais declarativo usando instrucções de alto nível como orderby e group by que dão ao motor de execução mais chances de uma melhor optimização da execução (paralelizando-a, por exemplo).
-
Concorrentes
A concorrência é muito difícil e necessita de ser bem pensada o que a torna muito difícil de “enfiar” numa linguagem de programação. O Parallel.For (das parallel extensions) tem o aspecto de um for paralelo porque foi dada suficiente expressividade ao C# 3.0 para permitir utilizações destas sem ter se usar uma sintaxe específica da linguagem.
-
Dinâmicas
Tem havido muito debate acerca qual o melhor tipo de linguagem de programação: estaticas or dinâmicas. O facto é que ambas têm boas qualidades e os utilizadores de ambas querem ter todas as funcionalidades à sua disposição.
Todas estas novas tendências requerem uma mudança de paradigma. O C# já é, em muitos aspectos, uma linguagem multi-paradigma. Ainda é uma linguagem muito orientada a objectos (ou a classes, segundo algumas opiniões), mas pode-se argumentar que o C# 3.0 se tornou uma linguagem de programação funcional porque tem todas as bases do que uma linguagem de programação funcional necessita. De futuro, terá ainda mais.
Além da influência destas tendências, houve uma decisão de co-evolução das linguagens de programação C# e Visual Basic. Desde o início que tem havido algum esforço em posicionar o C# e o Visual Basic em campos contrários e tentativas de justificar o que deve ser feito como uma ou outra linguagem de programação. Cada linguagem deve ser excolhida de de acordo com a experiência e familiaridade de programadores/equipas/projectos/empresas e não por qualquer funcionalidade em particular.
No passado, sempre que uma nova funcionalidade era adicioanda a uma das linguagens, os utilizadores da outra também a queriam para a sua lingaugem de opção. De futuro, sempre que uma funcionalidade for adicionada a uma das linguagens, a outra fará todos os esforços para também a adicionar. Ist não quer dizer que os literais XML serão adicionados ao C# (porque se pode obter quase o mesmo com o LINQ To XML), mas o Visual Basic terá propriedades auto-implementadas.
A quase totalidade destas funcionalidades requer ou é construída sobre funcionalidades da .NET Framework e, o foco para o C# 4.0 foi a rogramação dinâmica. Não apenas tipos dinâmicos, mas a capacidade de falar com qualquer coisa que não seja uma classe.NET.
Também introduzido com C# 4.0 é a co-variância e contra-variância para interfaces e delegates genéricos.
Fiquem atentos para mais sobre as novidades do C# 4.0 em entradas futuras.

Na minha última entrada apresentei o PredicateEqualityComparer e um método de extensão Distinct recebe um predicado e cria internamente um PredicateEqualityComparer para filtrar elementos.
Usar o predicado melhora a legibilidade, concisão e expressividade das consultas, mas ainda pode ser melhor. Na maioria dos casos, não queremos providenciar um método de comparação mas apenas extraír a chave de comparação dos elementos.
Por isso desenvolvi o SelectorEqualityComparer que recebe um método para extraír a chave do elemento. Algo como isto:
public class SelectorEqualityComparer<TSource, Tkey> : EqualityComparer<TSource>
where Tkey : IEquatable<Tkey>
{
private Func<TSource, Tkey> selector;
public SelectorEqualityComparer(Func<TSource, Tkey> selector)
: base()
{
this.selector = selector;
}
public override bool Equals(TSource x, TSource y)
{
Tkey xKey = this.GetKey(x);
Tkey yKey = this.GetKey(y);
if (xKey != null)
{
return ((yKey != null) && xKey.Equals(yKey));
}
return (yKey == null);
}
public override int GetHashCode(TSource obj)
{
Tkey key = this.GetKey(obj);
return (key == null) ? 0 : key.GetHashCode();
}
public override bool Equals(object obj)
{
SelectorEqualityComparer<TSource, Tkey> comparer = obj as SelectorEqualityComparer<TSource, Tkey>;
return (comparer != null);
}
public override int GetHashCode()
{
return base.GetType().Name.GetHashCode();
}
private Tkey GetKey(TSource obj)
{
return (obj == null) ? (Tkey)(object)null : this.selector(obj);
}
}
Agora já posso escrever código como este:
.Distinct(new SelectorEqualityComparer<Source, Key>(x => x.Field))
E, para uma melhor legibilidade, concisão e expressividade e suporte para tipos anónimos:
public static IEnumerable<TSource> Distinct<TSource, TKey>(this IEnumerable<TSource> source, Func<TSource, TKey> selector)
where TKey : IEquatable<TKey>
{
return source.Distinct(new SelectorEqualityComparer<TSource, TKey>(selector));
}
E agora uso apenas este código:
.Distinct(x => x.Field)
Para a maioria dos casos, é mais simples que usar um predicado.