sexta-feira, 14 de setembro de 2007

Um Try..catch em cada método

Muitos desenvolvedores acham que o ideal é colocar um try...catch em todo método. É comum ver blocos assim :

try
{
{Código}
}
catch(Exception ex)
{
throw ex;
}

No exemplo acima, o try .. catch é inútil, pois o efeito seria exatamento o mesmo, caso não se usasse try.. catch. Nos dois casos, quando um erro ocorresse, esse erro seria lançado para o método superior, a diferença no caso do exemplo acima é que o erro seria lançado duas vezes, uma na origem do erro e outra no comando throw ex.

A própria Microsoft recomenda que o catch seja utilizado apenas quando alguma coisa for feita com a exceção, ou seja, quando a presença do try..catch fizer alguma diferença. Caso contrário será uma sobrecarga desnessária.

Concordo que seja necessário tratar todos os erros, mas isso não precisa ser feito em todas as funções, se você tratar o erro nas funções principais, os erros das subrotinas também serão capturados.

segunda-feira, 27 de agosto de 2007

Classe ReadOnlyCollection

Recentemente tive um problema que outros já podem ter tido.

Tinha uma classe com uma coleção (List<>) private que queria disponibilizar, mas queria que quem utilizasse a classe pudesse apenas ler o conteúdo da lista, sem alterá-la. Se eu simplesmente colocasse uma propriedade pública com a lista, ia permitir que alguém adicionasse ou removesse elementos dela.

Entre outras soluções que pensei para isso, descobri uma classe que ajuda bastante. A ReadOnlyCollection. Ela é na verdade um Wrapper para uma coleção. Vamos supor que você tem uma lista de string na sua classe:

private List _linhas;

Para publicá-la como somente leitura , use :

public ReadOnlyCollection Linhas
{
get { return new ReadOnlyCollection (_linhas); }
}

Vale lembrar que esse método não duplica o conteúdo da lista. Como a classe é apenas um wrapper, qualquer alteração no conteúdo da lista será refletida no objeto ReadOnlyCollection já criado.

Classe ControlPaint

Uma classe interessante para quem está trabalhando com controles Owner Draw, ou seja, desenhando na mão o conteúdo do controle, é a ControlPaint.

Ela possui métodos para facilitar o desenho de controles, como o método DrawBorder3D (Acho que não preciso dizer que esse método desenha uma borda em 3D) e métodos para desenhar botões, checkboxes etc.

sexta-feira, 17 de agosto de 2007

Breakpoints Avançados

O tradicional Breakpoint, usado para interromper a execução do sistema e permitir a depuração do sistema possui algumas opções que podem ajudar bastante.

Você já teve que depurar algum loop e ficar apertando F10 até chegar na iteração que está dando problema? Você pode configurar o breakpoint para interromper a execução somente na n-ésima vez que passar por ele. Para isso, clique com o botão direito em um breakpoint existente (Clique na bolinha vermelha) e vá em "Hit Count...". Existem algumas opções para parada, uma pra parar quando o número de passados pelo breakpoint atingir um determinado valor ou for múltiplo de um determinado número , por exemplo.

Outra opção interessante é "Condition..." , acessada do mesmo popup menu, que permite que estabeleça um condição para que o Breakpoint pare. Você pode, por exemplo, fazer com que o breakpoint pare quando um totalizador atingir determinado valor, colocando uma expressão como "total > 100". A idéia é que se a expressão que você colocar for True, o Breakpoint será interrompido.

terça-feira, 14 de agosto de 2007

Uma outra forma de TypeCasting.

Provavelmente você já fez um TypeCasting, como o da linha abaixo:

DataSet ds = (DataSet) dataGridView1.DataSource;

Na forma acima, se o valor que está sendo convertido, no caso o dataGridView1.DataSource, for nulo ou não for um DataSet, será gerado um erro. Você teria que fazer um tratamento antes de efetuar o cast, usando o operador is para verificar se é realmente do tipo esperado:

if (dataGridView1.DataSource != null && dataGridView1.DataSource is DataSet)
{
DataSet ds = (DataSet) dataGridView1.DataSource;
}

Uma outra forma é fazer o cast com o operador as. Com esse operador, se o valor convertido for nulo ou não for do tipo esperado, será retornado nulo. Com isso, o código acima poderia ser escrito assim:

DataSet ds = dataGridView1 as DataSet;
if (ds != null)
...

Além disso, por não usar parenteses, pode deixar a sintaxe um pouco mais limpa, como no exemplo abaixo:

DataTable tbl = ((DataSet) dataGridView1.DataSource).Tables[0];

poderia ser representada com um conjunto de parênteses a menos usando o as :

DataTable tbl = (dataGridView1.DataSource as DataSet).Tables[0];

segunda-feira, 13 de agosto de 2007

Combo Box Somente Leitura

Quem programa em windows já deve ter se deparado com um situação onde precisava colocar uma ComboBox em modo somente leitura. No .Net não existe a propriedade ReadOnly na ComboBox. Creio que isso seja uma limitação do próprio windows, pois acontecia a mesma coisa no Delphi.

Uma solução normalmente adotada é deixar a combo desabilitada, o que não é muito bom, pois dessa forma o usuário não consegue navegar no controle com o Tab nem selecionar um texto para copiar.

Uma sugestão seria ao invés de desabilitar a combo , trocá-la por uma TextBox ReadOnly enquanto o usuário não puder alterar o valor. Quando habilitar o campo novamente para o usuário, retorna a ComboBox.

É , eu sei, é uma gambiarra, mas o resultado final é melhor (pelo menos para o usuário) do que simplesmente desabilitar.

segunda-feira, 6 de agosto de 2007

Escondendo uma TabPage de um TabControl

Se você desenvolve em Windows Forms, já deve ter passado por alguma situação onde era necessário esconder algumas páginas de um controles TabControl de acordo com a situação. O problema é que o TabControl não possui um método que permita apenas esconder as páginas. Será necessário removê-la no controle:

TabControl1.TabPages.Remove(tabPage1);

e para adicioná-la novamente :

TabControl1.TabPages.Add(tabPage1);

sexta-feira, 3 de agosto de 2007

BitConverter

Outra classe interessante que descobri recentemente foi a BitConverter. Quem já teve que trabalhar com classes Stream , para gravar ou ler um arquivo, por exemplo, já deve ter notado que os métodos para gravar e ler os dados trabalham com Array de byte. A classe BitConverter serve para converter valores primitivos, como int e boolean, de e para byte[].
Para converter um valor qualquer para byte[], use o método BitConverter.GetBytes(valor), e para o caminho contrário, use os métodos específicos, como BitConverter.ToInt32(byteArray).

Já se você tiver uma string e precisar converter de e para um array de byte, pode usar a classe Encoding. Aí vai depender se a string está em ASCII, UTF8, Unicode etc. Para converter de string para byte[], use Encoding.UTF8.GetBytes("String"), e para o caminho inverso, use Encoding.UTF8.GetString(byteArray). É só substituir o UTF8 pelo encoding apropriado.

quarta-feira, 13 de junho de 2007

Compilação Condicional - Modo Debug e Modo Release.

Quando você compila sua aplicação tem duas opções, compilar em modo debug ou modo release (Caso não saiba, essa opção fica ao lado do botão Start Debuggin, o "play").

A idéia é compilar em modo debug enquanto estiver desenvolvendo e depurando a aplicação, e em modo release quando for colocar o aplicativo em produção , pois para depurar o executável, o compilador precisa embutir nele algumas informações extras, como nome do arquivo fonte e número da linha. Como você dificilmente irá depurar diretamente o aplicativo em produção, essas informações podem ser removidas para otimizar o aplicativo.

Além dessa otimização que o próprio compilador faz, o próprio programador pode especificar uma determinada parte do código que só será executada em um modo específico. Por exemplo, você quer escrever uma rotina de tratamento de erros que não mostre a mensagem de erro original para o usuário, pode fazer algo do tipo:

try
{
// Código
}
catch(Exception ex)
{
#if DEBUG
MessageBox.Show(ex.Message);
#else
MessageBox.Show("Ocorreu um erro no sistema!");
#endif
}

No código acima , o primeiro comando MessageBox será executado se o programa for compilado em modo DEBUG, e o segundo se a aplicação for compilada em modo Release.

Além disse, você pode usar o Atributo Conditional para excluir uma determinada função do modo release. Se você declarar uma função dessa forma :


[System.Diagnostics.Conditional("DEBUG")]
private void Funcao()

Essa função só será considerada se estiver em modo DEBUG, caso contrário , será ignorada. A vantagem nesse caso, é que você não precisa alterar os pontos do códigos que chamam a função, somente a própria função.

terça-feira, 12 de junho de 2007

Stopwatch

Existe uma classe simples mas útil no namespace System.Diagnostics chamada Stopwatch. Essa classe simula um cronômetro e pode ser usada, por exemplo, para medir o tempo gasto por determinada operação. Por exemplo, você quer saber quanto tempo determinada função está demorando para executar, pode fazer isso:

Stopwatch sw = new Stopwatch();
sw.Start();
OperacaoDemorada();
sw.Stop();
Debug.WriteLine("Tempo gasto : " + sw.ElapsedMilliseconds.ToString() + " ms");


O código acima irá mostrar o tempo gasto em milisegundos, mas a classe também possui a propriedade Elapsed, que retorna um TimeSpan, permitindo a obtenção do tempo na unidade que você quiser.

domingo, 3 de junho de 2007

Acessando uma classe .Net via COM

A frequência de atualização desse blog tem sido baixa, mas ele ainda vive...rs. Vou tentar atualizá-lo com mais frequência. Dica: Para você saber quando há uma atualização, utilize um leitor RSS, como o FeedReader: http://www.feedreader.com/

Agora ao artigo.

Quem já trabalhou com ASP ou VB (os antigos, não-.Net), provavelmente já usou um comando como esse:
set obj = CreateObject("Dundas.Upload")
Deixando erros de sintaxe de lado (Escrevi só como exemplo, vai), esse comando permite criar objetos COM para acessar em sua aplicação. Você sabia que é possível chamar uma classe .NET dessa forma?

A vantagem é que com isso é possível acessar uma dll em .Net de uma aplicação ASP tradicional, VB 6 ou qualquer outra plataforma que suporte COM. Isso pode facilitar a transição parcial de um sistema para a plataforma .Net.

Passarei aqui as instruções para o Visual Studio 2005, mas isso também é possível no 2003. Em primeiro lugar é necessário adicionar uma strong name key ao seu projeto. Caso não saiba como fazer isso, procure por Strong Name Key no google que deve achar bastante coisa sobre o assunto. Também é necessário que todas as DLLs às quais a sua faça referência tenham um strong name key. Agora é só compilar e registrar a DLL.

Para registrá-la, use o utilitário de linha de comando Regasm.exe , com a seguinte sintaxe :
RegAsm.exe NomeDoArquivo.dll /tlb /codebase
Não sei te dizer exatamente o que fazem os parâmetro /tlb e /codebase, mas sei que sem eles não funciona :P (Quem souber e quiser explicar, fique a vontade e poste um comentário. ;) ). Como qualquer outro utilitário do .Net, o RegAsm.exe está na pasta do framework (e pode ser acessado diretamente se utilizado o atalho para o prompt de comando criado pelo visual studio no menu iniciar).

Após registrada a DLL, é só acessá-la do ASP ou VB como se fosse um objeto COM. Na string de identificação da classe, use o namespace seguido do nome da classe. No exemplo abaixo , está sendo chamada a classe TesteCOM do namespace Br1.Library.Teste:
set obj = CreateObject("Br1.Library.Teste.TesteCOM").

Algumas observações importantes:
  • Isso obviamente não elimina a necessidade do Framework, ele deve estar instalado na máquina onde o objeto COM for executado.
  • A DLL chamará as outras como qualquer objeto .Net, então as dlls às quais ela faz referência devem estar ou no mesmo diretório ou no GAC.
  • Só consegui fazer funcionar a DLL com tipos primitivos nos parâmetros e retorno. Internamente podem ser usadas classes e structs sem problema, mas a função que será chamada externamente só poder receber e retornar tipos primitivos, como string ou int.
  • Como qualquer objeto COM, depois da primeira chamada, a DLL vai estar carregada na memória. Portanto, para substituir a DLL pode ser necessário reiniciar o ISS. Sugiro registrar a DLL de outro local, para não dar erro quando o VS não conseguir compilá-la devido à dll em bin\debug estar em uso. Sugiro criar um .BAT para a atualização, com os seguintes passos:
  1. Reiniciar o iis (use o iisreset.exe)
  2. Desregistrar a DLL com o comando : Regasm.exe NomeDoArquivo.dll /unregister
  3. Excluir os arquivos .DLL e .PDB do diretório de registro.
  4. Copiar os arquivos .DLL e .PDB do diretório bin\debug (ou bin\release, se preferir)
  5. Registrar a DLL novamente com o comando que já passei.

Essa abordagem pode ser bem interessante para ir convertendo aos poucos um sistema para .Net, ou então para incrementar um sistema já existente.

quarta-feira, 30 de maio de 2007

[Off-Topic] Microsoft Surface

Não é relacionado com programação diretamente, mas aposto que vão gostar. É um futuro produto da microsoft que parece ser muito interessante. Um computador cuja tela fica na horizontal , como uma mesa, por exemplo, e é controlado totalmente por touchscreen.

Para copiar músicas para um Mp3 player , por exemplo, coloca-se o aparelho em cima da tela e arrasta-se as músicas para ele. Fica bem melhor vendo, Assistam os vídeos.

http://www.microsoft.com/surface/

sábado, 12 de maio de 2007

Tratamento de erros: Tratando e registrando erros

Nesse artigo vou falar sobre o tratamento de erros e registro dos mesmos. Anteriormente escrevi outros artigos sobre o object Exception e o tratamento de erros no .Net :

http://br1dev.blogspot.com/2007/04/tratamento-de-erros-objeto-exception.html
http://br1dev.blogspot.com/2007/04/tratamento-de-erros-objeto-exception_10.html
http://br1dev.blogspot.com/2007/04/tratamento-de-erros-objeto-exception_16.html

Agora vamos ao artigo:

Um fator que melhora a confiabilidade da sua aplicação é a maneira com ela trata os erros que ocorrem durante sua execução. É claro que o ideal é evitar o máximo possível que erros ocorram, mas é necessário que a aplicação se recupere adequadamente quando um erro ocorrer. Achar que sua aplicação está segura e não terá erros, e por isso abrir mão de um tratamento adequado, pode expor sua aplicaçõe a riscos de segurança e integridade da aplicação.

Sempre que um erro ocorrer na aplicação, são importantes os seguintes procedimentos:


  • Recuperar o sistema - Sempre que possível, o sistema deve retornar ao estado em que estava antes da execução da operação que gerou o erro. Um exemplo clássico disso. Você confirma o recebimento de uma pedido de venda. A operação atualiza o estoque , mas quando tenta alterar o status do pedido , ocorre um erro. Se o estoque não for retornado ao estado anterior, o usuário pode tentar repetir a operação (Afinal , para ele houve um erro e a operação não foi concluída) e o estoque será alterado novamente, ficando com valores errados.
  • Informar o usuário - Evite esconder um erro, não mostrando ao usuário que houve um erro. Exceto em casos específicos, é sempre importante avisar o usuário que o sistema não se comportou da forma esperada, para que ele possa corrigir o problema ou pelos contatar o suporte. Entretanto, uma mensagem do tipo "Object reference not set to an instance of an object" não ajuda em nada o usuário. O ideal é mostrar ao usuário uma mensagem padrão, do tipo "Ocorreu um erro no sistema, contato o suporte técnico etc.", ou então alguma coisa que realmente o ajude a solucionar o problema.
  • Registrar o erro - Se por um lado não é interessante mostrar ao usuário detalhes do erro, também não é uma boa ignorar esses detalhes por completo. O ideal é registrar a mensagem original do erro, e todos os outros detalhes possíveis a fim de auxiliar a correção do erro pelos desenvolvedores. Devem ser registradas informações como a pilha de chamadas, que mostra toda a cadeia de chamada de métodos até chegar no local do erro, o nome do erro (ArgumentException, por exemplo) e quaisquer outras informações que estejam disponíveis. Você sabia que com o objeto SQLExcetion é possível obter as mesmas informações que aparecem quando você executa a procedure ou comando no Query Analyzer?. Outra informação importante é a InnerException. No terceiro artigo que mencionei no começa desse post falo mais sobre ela. Para registrar o erro você poderia gravar em um arquivo texto, um arquivo XML, enviar por email ou gravar em um banco de dados. O importante é escolher o mecanismo que torne mais fácil o acesso a essas informações.

Pra finalizar, você poderia tornar esse tratamento mais fácil criando uma função que recebe o objeto Exception e faça o registro e exibição da mensagem referente ao mesmo. Aí, é só chamar a função em todo bloco catch da aplicação.

quinta-feira, 3 de maio de 2007

[Off-Topic] Google AdSense

Esperava mais do Google AdSense. Achei que ele mostrava propagandas que tivessem relação com o conteúdo do site, mas no meu blog só tá mostrando anúncios para se livrar do alcoolismo e propagandas de Fossas Sépticas! :

Começando em .Net e C#

Desculpem pela demora na postagem, as coisas estão meio corridas pra mim ultimamente.

De vez em quando vejo emails em listas de discussão, ou mesmo alguém me pergunta como começar no .Net . Resolvi então colocar algumas instruções .

Em primeiro lugar, o .Net fornece uma estrutura para o desenvolvimento e execução de aplicações gerenciadas. Gerenciada porque durante a execução da aplicação, o framework controla alguns recursos que a aplicação utiliza, como conexão com o banco de dados e memória utilizada.

Aplicações para esse framework podem ser criadas em diversas linguagens, sendo as principais, que são suportadas pela Microsoft são o C# e o VB.Net. A diferença entre essas duas linguagens é apenas na sintaxe. Todos os recursos do Framework estão disponíveis para qualquer linguagem, então escolha a que melhor agradar. Minha sugestão, aprenda as duas. Não é algo difícil, afinal a base é a mesma, e você terá muito mais flexibilidade profissionalmente.

O Importante então, é aprender o framework, sua estrutua principal.

Um bom lugar pra começar é Programa Desenvolvedor 5 estrelas. Esse programa consiste em uma série de treinamentos, na forma de apresentações com narração, que você pode baixar e estudar. São 3 estrelas principais (as duas últimas se referem à certificação). Cada nível possui um grau diferente de dificuldade e possuem provas online para se certificar que você assimilou o conteúdo do nível (não vale colar, hein :) ) . Acho que não preciso dizer que o ideal é começar do nível 1, e quando tiver dominado os conceitos apresentados, partir para o nível seguinte.
Endereço : http://www.msdnbrasil.com.br/cinco_estrelas/#Treinamento

Depois de ter estudado o programa acima, dê uma olhada no link abaixo, que possui alguns tutoriais interessantes para quem está começando. Ou mesmo para quem já conhece alguma coisa e quer saber algo mais sobre Datagrids, por exemplo.
Endereço : http://www.microsoft.com/brasil/msdn/Tutoriais/default.mspx

Quando estiver um pouco mais experientes, dê uma olhada nos Webcasts da microsoft. Para quem não sabe, webcast é uma apresentação online, onde você além de assitir a apresentação de slides, ouve o apresentador falando e, pelo menos em alguns casos, pode fazer perguntas a ele. Além disso, depois da realização do webcast, você pode fazer o download da apresentação para assistir depois . Claro que nesse caso não vai conseguir perguntar, a não ser que mande um email pro cara. :)
Endereço : http://www.microsoft.com/brasil/msdn/Eventos/Webcasts.mspx

Bom, é isso. Claro que há muito mais material disponível, mas com esses já dá para ser introduzido (sem duplo sentido, por favor) no mundo .Net. É importante que durante seu estudo, na medida do possível, você ponha a mão na massa. Crie programas que apliquem o que você estudou, assim irá fixar e aprender bem melhor.

E sempre haverá o Google ;)
Endereço: Precisa? Tá bom : http://www.google.com.br

segunda-feira, 23 de abril de 2007

[Vista] Obtendo o endereço IP da máquina no ASP

Para se obter o endereço IP da máquina cliente no Asp tradicional, pode-se consultar o valor da variável Request.ServerVariables("REMOTE_ADDR").

Porém percebi algo nas minhas tentativas de programar no Windows Vista. No Windows XP funcionava normal, retornava "127.0.0.1", quando executava da minha máquina. No Windows Vista, quando acesso esse valor, retorna "::1". Não sei o motivo, talvez seja culpa do ISS7, ou alguma configuração do Vista que protege o endereço IP. O Fato é que se a sua a aplicação consulta o IP da máquina, é bom prestar atenção nisso.

Não tentei acessar um servidor Vista de um cliente XP ou vice-versa, para isolar o problema no cliente ou servidor. Se alguém quiser fazer isso, poste um comentário com o resultado para compartilhar com os outros ;)

segunda-feira, 16 de abril de 2007

Tratamento de erros: Objeto Exception parte 3

InnerException

Nos dois últimos artigos falei sobre o objeto exception e suas principais características. Agora, para concluir, irei falar sobre uma propriedade importante do objeto Exception, a InnerException. Essa propriedade não é utilizada por muitas exceções, mas quando é utilizada, é importante analisá-la para obter informações mais completas a respeito do erro.

Se queremos tratar um erro apenas para adicionar informações extras a ele, mas sem perder o erro original, podemos encapsular esse erro em outro objeto exception. O erro original será referenciado pela propriedade InnerException desse erro original.

Como um exemplo da aplicação dessa técnica, imagine que esteja criando uma classe base de acesso a dados, com métodos como ExecutarProc ou coisa do tipo. Se você quiser que , quando um erro ocorrer ao executar uma proc, você queira informar o nome da procedure chamada, pode capturar o erro que ocorreu durante a execução, criar um novo erro com o nome da proc, e encapsular o erro original na propriedade InnerException do erro criado. Em um artigo futuro, irei falar sobre como criar suas próprias exceções, então falarei mais sobre InnerException.

Um exemplo no framework onde isso é utilizado, é na classe XmlSerializer. Esse classe server para gravar e carregar objetos em formato XML. Quando você carrega um xml com esse objeto, e o xml está mal formado, irá obter uma mensagem do tipo : "There is an error in the XML Document (Há um erro no documento XML)". Só essa mensagem não te ajuda muito a descobrir o problema com o arquivo. Mas se você analisar a propriedade InnerException da exceção gerada, verá outro objeto Exception com a mensagem que descreve exatamente qual o problema no xml.

Não existe um limite para a propriedade InnerException, pode ser criada uma cadeia de exceções, cada uma fornecendo um detalhe adicional sobre o erro. É importante, ao criar o mecanismo de tratamento de erros da sua aplicação analisar a propriedade InnerException da exceção (e das próprias InnerException), pois podem haver informações importantes lá.

quarta-feira, 11 de abril de 2007

[Vista] Permissão de administrador

O Windows vista restringe os acessos dos programas quando executados normalmente. Algumas vezes será necessário rodar o programa como privilégio de administrador para funcionar adequadamente.

Para fazer isso, clique com o botão direito no atalho ou no executável e clique em "Executar como Administrador". Para não ter que fazer isso toda hora, vá nas propriedades do atalho, na aba Compatibilidade e marque a opção "Executar esse programa como administrador.

Quando você executa o Visual Studio 2005 ele recomenda que você faça umas das opções acima. O SQL Server Management Studio Express permite que você entre no programa, mas algumas tarefas , como anexar um novo .mdf e criar uma base de dados, requerem a permissão de administrador.

terça-feira, 10 de abril de 2007

Tratamento de erros: Objeto Exception parte 2

No post anterior sobre exceções, falei sobre o objeto Exception e como utilizá-lo para obter informações sobre erros ocorridos no sistema.


Agora falarei um pouco mais sobre esse objeto, explicando como pode ser utilizado para obter informações específicas a determinados tipos de erro e explicarei um pouco mais sobre a estrutura e funcionamento do bloco try..catch.

O object Exception é , na verdade, a classe base para os objetos que são gerados em caso de erro. Para cada tipo de erro, haverá um objeto diferente, herdando de exception. Esse objeto, dependendo do erro, pode disponibilizar informações adicionais sobre o erro. Se o programa efetuar uma divisão por zero, por exemplo, a exceção DivideByZeroException será gerada. Se for um erro ao executar um comando em um banco SQL Server, você obterá uma SQLException. Essas exceções herdam do objeto Exception, e por isso, possuem todas suas características. Alguns até adicionam informações extras. Quando você executa uma procedure no SQLServer, por exemplo, vários erros podem ocorrer durante a execução. Com o SQLException é possível acessar uma lista desses erros, incluindo informações como a severidade do erro.

Para tratar um erro, como mostrei no último post, você usa a seguinte estrutura :

try
{
// Código a ser tratado
}
catch(Exception ex)
{
// Código que irá tratar o erro.
}

Nesse exemplo, todo tipo de erro será capturado no mesmo bloco. Você pode, porém, criar blocos separados para cada tipo de erro, dessa forma:

try
{
// Código que pode gerar o erro
}
catch(SQLException ex)
{
// Código para tratar erros do SQL Server
}
catch(DivideByZeroException ex)
{
// Código para tratar erros de divisão por zero
}
catch(ArithmeticException ex)
{
// Código para tratar outros erros aritméticos.
}
catch(Exception ex)
{
// Código para tratar outros tipos de erro.
}
finally
{
// bloco que será executado dando ou não erro.
}

No bloco acima, trato vários tipos de erro. Quando um erro ocorre, ele é direcionado para o primeiro bloco catch que atenda a esse erro. É importante lembrar que um bloco Catch com uma determinada exceção, atende a esse exceção e a todas as exceções que herdarem dessas. Por isso, é importante que a ordem dos blocos a ser mantida seja do mais específico para o mais genérico. Se o Catch(Exception ex) estive no início , iria capturar todas as exceções (pois todas elas são exception, ou herdam de Exception), e nenhum dos outros blocos seria executado.

O bloco Finally será sempre executado, idependente de acontecer ou não um erro.

O Fluxo da execução ao acontecer um erro será:
  • Será executado apenas o primeiro bloco catch que atenda a exceção lançada, ou que atenda a umas das classes da hierarquia da qual a exceção herda.
  • O bloco finally será executado, caso haja um.
  • Se algum dos blocos catch atender à exceção, o erro será considerado tratado, e execução continuará do ponto após todo o bloco try..catch.
  • Se não houve um bloco catch que atendesse à exceção, a execução do método atual será interrompida, e o erro será lançado para o bloco try..catch acima, ou para o método que chamou. (Se houver um bloco finally, ele será chamado de qualquer forma).
Quando você escreve um bloco catch para tratar um erro, o erro será considerado tratado ali e após o bloco try..catch a execução continuará normalmente. Se você quiser tratar o erro, mas quiser que o erro ainda seja lançado para o método chamador, pode usar o comando throw, dentro do bloco try..catch.

try
{
// Comandos
}
catch (Exception Ex)
{
// Trata o erro
throw; // Repassa o erro
}

Porém, só faça isso se for fazer alguma coisa com o erro. Se for simplesmente repassar, como abaixo:

try
{
// Comandos
}
catch(Exception ex)
{
throw;
}

Isso não terá a menor funcionalidade, pois esse já é o comportamento padrão. E evite , acima de tudo :) , tratamentos que simplesmente ignorem o erro:

try
{
// Comandos
}
catch(Exception ex)
{}


Isso seria o equivalente a solucionar o problema daquele barulhinho estranho no seu carro com um tapa ouvidos. Você não perceberia mais o problema, mas ele estaria lá, e uma hora a coisa vai complicar.

sábado, 7 de abril de 2007

[Vista] Asp Tradicional: "Caminho Pai Não Permitido"

Uma mensagem que pode aparecer ao tentar executar uma aplicação ASP tradicional no Windows Vista : "Caminho Pai Não Permitido". Parece que por padrão, o ISS7 não permite que se use os dois pontos (..) para referenciar diretórios, como em Response.Redirect("..\clientes\cadastro.asp").

Para corrigir, ou você muda a referência ao diretório para não usar caminhos relativos, ou habilita esse tipo de referência no ISS:
  • Vá no administrador do ISS.
  • Selecione seu site ou diretório virtual
  • Clique em ASP.
  • Mude a configuração "Habilitar caminhos pai" para True.

quarta-feira, 4 de abril de 2007

Tratamento de erros: Objeto Exception parte 1

Vou explicar nesse e nos próximos artigos como funciona o tratamento de erros em aplicações .Net. Vou explicar o funcionamento do tratamento estruturado de exceções, o objeto Exception e como criar suas próprias exceções.

O .Net adotou o modelo de tratamento de erros estruturado, através de objetos representando os erros ocorridos, já utilizado em outras linguagens orientadas a objeto, como Delphi e Java.

Nesse primeiro artigo vou explicar o objeto Exception e como utilizá-lo para obter as informações necessárias a respeito do Erro. Nos próximos artigos tratarei outros aspectos do tratamento de erros, como a propagação do erro na hierarquia de chamada (se não entendeu, tudo bem, vou explicar depois) e a criação de suas próprias exceções.

Para capturar o erro de um código, coloque o código que deseja tratar dentro de uma estrutura try..catch, como mostrado no exemplo abaixo:

try
{
// Código que pode gerar o erro
}
catch(Exception ex)
{
// Aqui você pode tratar o erro com o objeto ex
MostrarMensagem(ex.Message);
}

No exemplo acima, se ocorrer algum erro entre o try e o catch, esse erro será tratado no bloco catch. Note que a propriedade Message do objeto ex é acessada para obter a mensagem de erro. Existem outras propriedades no objeto Exception que podem ajudar na correção do erro. Cito aqui as mais importantes:

Message
A mesma mensagem de erro que é exibida quando o erro não é tratado.

Source
A Fonte do Erro. Normalmente indica a biblioteca ou componente que gerou o erro.

StackTrace
Mostra a pilha de chamadas até o local do erro. É muito útil para encontrar o local do erro. Vai conter um texto com várias linhas como o texto abaixo:

at System.Number.ParseInt32(String s, NumberStyles style, NumberFormatInfo info)
at System.Int32.Parse(String s, NumberStyles style, IFormatProvider provider)
at System.Int32.Parse(String s) at System.Convert.ToInt32(String value)
at MyApp.Form1.btn1_Click(Object sender, EventArgs e)
in C:\Projetos\form1.cs:line 194
(São 4 linhas , a última quebrou por causa do tamanho disponível no blog).

A melhor maneira de ler essa lista é do fim para o início. No exemplo acima, você consegue indentificar que o processo começou no evento onclick do botão btn1 que, por sua vez, chamou, na linha 194 o método System.Convert.ToInt32, que chamou o System.Int32.Parse e assim por diante.

Capturando o erro, você poderia tratá-lo de uma maneira mais adequada, por exemplo, mostrando uma mensagem padrão para o usuário e gravando em um arquivo de log a mensagem de erro real e o StackTrace.

No próximo post irei falar mais do objeto Exception, explicando a estrutura de herança e como obter informações mais detalhadas sobre certos tipos de erro.

sábado, 31 de março de 2007

[Vista] Que contas de novo?

Esse não tem exatamente a ver com programação mas eu tinha que mostrar. Quando você instala alguma aplicação no Vista, ele pode exibir uma mensagem dizendo que o software não é compatível com essa versão do Windows. No caso do Norton Antivírus, pelo visto deve haver algum problema nos balancetes da Symantec:







[Vista] Rodando aplicações ASP tradicionais

Estou tentando fazer uma aplicação ASP tradicional rodar no windows vista sob o IIS7. O primeiro problema que encontrei foi que ao rodar estava aparecendo a seguinte mensagem "An error occurred on the server when processing the URL. Please contact the system administrator". Depois de fuçar um pouco, descobri que há uma configuração no IIS7 para esconder os erros de asp:
  • Abra a ferramenta de administração do IIS.
  • Selecione seu site ou seu diretório virtual e clique no botão ASP.
  • No item Compilação / Propriedades de Depuração , Mude a opção "Enviar erros ao navegador" para True. Aliás, aproveite e mude todos esses subitens para True.
  • Clique no botão Aplicar.
  • Pronto, agora rode novamente a aplicação e você verá a mensagem real de erro.

sexta-feira, 30 de março de 2007

Visão do Desenvolvedor vs Visão do usuário


Alguma vez você já disse, ou ouviu alguém dizer "Foi o usuário que fez merda"? Creio que sim. Bom, talvez não usando essas plavaras (rs), mas muitas vezes o usuário é culpado pelo uso errado do sistema.

Na minha opinião, na maioria das vezes que o usuário utiliza o sistema de forma errada, a culpa é do sistema, ou pelo menos uma parcela da culpa.

O problema é que costumamos desenvolver sistemas do ponto de vista da tecnologia, e não do usuário. O que pra nós é claro e fácil, para o usuário pode ser bastante confuso, e induzir a erro. Nós desenvolvedores e analistas possuimos uma série de conhecimentos técnicos sobre o funcionamento de um software que a grande maioria dos usuários não têm. E nem precisam ter. Você precisa conhecer o funcionamento da sua TV ou aparelho de som para utilizá-los?

Um exemplo simples, mas que ilustra bem esse problema é a questão de campos para informar data. Não é incomum um site ou programa que exijam que a data seja digitada no formato dd/mm/aaaa.

Algumas vezes essa exigência é informada ao usuário apenas exibindo a máscara, e espera-se que ele vá entender o que isso significa, outras vezes, nem é informada e o usuário que tente descobrir porque sua data não é válida.

Na visão de desenvolvedor, é fácil entender que a data deve ser 01/04/2007, mas para uma "pessoa comum", uma data pode ser apresentada de várias formas. Se você ver em algum lugar a data "1/4", vai reconhecer como uma data válida, já entendendo como o ano atual. Se um amigo seu te fala "Dia Primeiro agora haverá um churrasco lá em casa", você vai entender isso como uma data válida, e vai saber que ele se refere ao dia 1 do próximo mês. Todas essas são datas válidas. Ou por acaso quando fala uma data para alguém você fala "Olha, dia Zero Um do Zero Quatro de Dois mil e Sete vai ter um churrasco lá casa"?

Não estou dizendo que nesse caso você deveria fazer o programa reconhecer o texto "Próximo dia 1°" como uma data válida (Embora, se fizesse isso, ia ficar bem legal :) ). Mas se o usuário digitar 1/4/07, por que não tratar a data e transformá-la numa data aceitável para o computador? Ou mesmo "1/4", podemos subentender como "01/04/2007".

Esse caso da data foi só um exemplo, para mostrar como o software pode dificultar o uso se ater-se demais ao modelo tecnológico ao invés do modelo do usuário. Em muitos casos que o usuário executou um operação inválida no sistema, ou entrou com dados incorretos, o sistema poderia validar essas operações e informações e avisar ao usuário antes, afinal de contas, se é inválido, porque o sistema deixou fazer?

Creio que podemos reduzir bastante os erros do usuário apenas dando uma atenção melhor à nossas interfaces. Uma boa experiência é observar ocasionalmente o usuário enquanto ele estiver utilizando o programa. Dessa forma, é possível perceber melhor quais pontos do sistema podem ser melhorados para facilitar a tarefa do usuário.

quarta-feira, 28 de março de 2007

Windows Vista: A missão.

Recentemente comprei o Windows Vista Home Premium para o meu computador de casa. Como sou desenvolvedor, claro que vou querer programar nele também. Então, conforme for avançando vou postando aqui os problemas que forem ocorrendo e como solucionei ou porque ainda não encontrei uma solução, para ajudar quem queira usar o Vista e enfrente problemas semelhantes.

Visual Studio
Depois de instalar o Vista, instalei o Visual Studio 2005. O próprio Vista me informou que há um problema de compatibilidade com o Visual Studio 2005 e que seria necessário baixar um service pack. Ainda não baixei o service pack, e o VS parece estar funcionando. Porém, até agora só desenvolvi um console application de teste, ainda não criei uma aplicação Asp.Net pra ver como se comporta. De acordo como uma pesquisa rápida que fiz na internet, o VS 2003 não é suportado pelo Vista :( . Agora, não sei se o "não é suportado" significa "esquece, não funciona mesmo, meu filho" ou "Funcionar até funciona, mas não nos responsabilizamos nem garantimos nada". Posteriormente vou tentar instalar o 2003 , então postarei os resultados.

IIS
O ISS não vem instalado por padrão, mas pelo menos a partir da versão Home Premium o IIS 7 vem disponível no disco de instalação, é só adicionar. Não sei se está disponível na versão Home Basic, e provável que não. Durante a instalação, você escolhe o que vai querer rodar no IIS: Asp, Asp.Net, CGI etc. Escolhi Asp e Asp.Net e a instalação concluiu numa boa. Acredito que nem será necessário o famoso aspnet_regiis.exe para registrar o Asp.Net no iis.

segunda-feira, 19 de março de 2007

Porque StringBuilder é mais rápido que concatenação de strings

Desculpem a demora para postar, estava tentando colocar um trecho de código aqui no blogger, mas isso estava se mostrando uma tarefa complicada, acabei desistindo.

Você provavelmente já ouviu falar ou já leu em algum lugar sobre o uso de StringBuilder ao invés de concatenação de strings. Realmente o objeto Stringbuilder é bem mais rápido para concatenar grandes quantidades de strings. Duvida? Faça um teste. Faça um loop concatenando strings 10.000 vezes e calcule o tempo. Em seguida, faça a concatenação usando StringBuilder. Pra se ter uma idéia, fiz um teste na minha máquina e quando concatenava 10.000 vezes, demorou uns 2 segundos. Quando fiz a concatenação com o StringBuilder, e fazendo 100.000 vezes, demorou apenas alguns milésimos.

Porquê o StringBuilder é tão mais rápido? Por causa da forma como trata a alocação da memória.

Vejamos a seguinte operação:

StringC = StringA + StringB;

Nesse caso, será alocado um espaço na memória suficiente para caber a StringA e a StringB, e o conteúdo dessas duas strings será copiado para esse novo espaço alocado. Se fizermos outra alocação:

StringC = StringC + StringD;

Será alocado mais um espaço na memória, que caiba as duas strings e o conteúdo das duas será copiado. A cada nova concatenação feita, o processo se repetirá. Será feita uma alocação de memória para cada string adicionada.

No Caso do StringBuilder, a história é diferente. Quando um objeto StringBuilder é instanciado, já aloca por padrão 16 bytes para a string (Esse valor inicial pode ser alterado através de um parâmetro do construtor). Quando uma string é adicionada:

sb.Append(StringA);

O StringBuilder utilizará o espaço já alocado e copiará a string informada para lá. Novas strings concatenadas serão adicionadas ao mesmo espaço. Quando o espaço disponível chegar ao fim, o objeto irá alocar um novo espaço, com o dobro do tamanho do anterior e copiar o conteúdo para lá. Com essa abordagem, a quantidade de alocações de memória reduz significativamente. E como o StringBuilder sempre aloca um espaço com o dobro do tamanho do anterior, a frequência das alocações diminui conforme as strings são adicionadas.

Vale lembrar que o StringBuilder serve não somente para concatenar strings, mas para manipulá-las. Você pode inserir um texto em determinado ponto, excluir partes da string, substituir, tudo isso usando o espaço já alocado.

domingo, 18 de março de 2007

"Cannot start Debugging on the server"

Sua aplicação estava funcionando normalmente e "de repente", ao executar começou a aparecer a mensagem "Cannot start Debugging on the server"? Isso pode ser um erro que está ocorrendo na inicialização da aplicação, provavelmente no Web.Config.

Inicie sua aplicação pela opção "Start Without Debugging". Se for mesmo erro no web.config, será mostrado. Corrija o erro e depois pode voltar a usar o F5 normalmente.

sábado, 17 de março de 2007

TypeCast e Conversão

Surgiu uma dúvida num grupo de discussão sobre o assunto e resolvi falar sobre a diferença entre TypeCast e Conversão de tipos.

Conversão de tipos ocorre quando você tem um valor de um determinado tipo e deseja convertê-lo para outro tipo. Por exemplo, você tem uma string e deseja convertê-la para um número.

Já o TypeCast ocorre quando você tem um valor de um tipo, mas está sendo tratado como um tipo superior. Por exemplo, alguns controles de uma página web possuem o método FindControl, para o qual você passa o nome de um controle da página e retorna um referência para o controle. Por ser um método que irá funcionar para qualquer tipo de controle, ele irá retornar um objeto do tipo Control. Na verdade ele irá retornar algum objeto que herda de Control. Para usar o objeto retornado , você terá que especificar qual é o tipo desejado:

Button btn = (Button) FindControl("btnConfirmar");

O Nome da classe entre parênteses indica que você sabe que o retorno será um Button (Se não ocorrerá um exceção).

Para exemplificar a diferença entre eles, vamos supor que temos um DataRow "dr" com um campo "Codigo". Podemos obter o valor desse campo de duas formas:

int codigo = (int) dr["Codigo"];
int codigo = Convert.ToInt32(dr["Codigo"]);

Na primeira opção, não é feita nenhuma conversão de tipos, apenas o TypeCast. Se o campo "Codigo" não for do tipo int, será gerado um erro. Mesmo se for outro tipo inteiro como short, por exemplo. Já a segunda opção, efetua a conversão do valor do campo para Inteiro. Só ocorreria erro aqui se o conteúdo do campo fosse algo que não pudesse mesmo ser convertido, como a string "João", por exemplo.

Qual é melhor utilizar? Se o campo já for int, use TypeCast, caso contrário, use a conversão.

sexta-feira, 16 de março de 2007

Javascript, Terra de ningúem!

Javascript é uma das linguagens com menos restrições para executar o código, tudo vale.

Por exemplo, se você declarou uma função com dois parâmetros. Ao chamá-la, se passar 1 parâmetro, vai funcionar, se passar 2 também vai, se passar 3, 4 ou mais parâmetros, também fuciona...

Outra coisa que costuma gerar erros é errar nome de variável. Por exemplo, para alterar o texto de uma textbox, via javascript, o correto seria :

Form1.tbxPesquisaDataPedido.value = "texto";

Se você escrever errado, e ao invés de value, escrever vale, ou até mesmo Value (Javascript diferencia maiúscula de minúscula), não vai dar erro. Mas também não vai funcionar. O que o irá acontecer é que será adicionada uma variável chamada vale e o valor "texto" será atribuído a ela. Você só irá perceber o erro depois, e até rastreá-lo a origem vai ser trabalhoso.

Lembre-se disso quando procurar um erro em seu script.

Bem vindos!

Sejam bem vindos!

Nesse blog vou postar dicas, comentários e quaisquer outras informações que achar interessante a respeito de programação, principalmente na linguagem .Net. Pretendo colocar aqui algumas dúvidas de usuários em grupos de discussões que respondo, ou que outro usuário respondeu e achei interessante.

Estou aberto a sugestões, se você tiver algo a dizer sobre o blog, poste um comentário e diga o pensa. (Mas seja educado, por favor.. :) )