Arquivo
Instrumentação .NET – EventLog
No artigo de hoje irei demonstrar como usar o Visualizador de Eventos do Windows para instrumentar sua aplicação.
Para acessar o Visualizador de Eventos do Windows digite “eventvwr” no executar. Este gerenciador de eventos disponibiliza, na janela da esquerda, todos os EventLog cadastrados no computador, o próprio Windows utiliza eles para logar eventos do sistema. Já na janela da direita, quando você selecionar um EventLog deve aparecer uma lista de eventos que foram registrados pelo Windows. Preste atenção na coluna “Fonte”, ela indica de onde veio este evento, que no nosso caso virá do aplicativo criado.
Para criar um EventLog via código para ser usado pela sua aplicação devemos importar o namespace System.Diagnostics e utilizar a classe EventLog. Como no exemplo abaixo:
EventLog.CreateEventSource(“Fonte”, “TesteLog”);// Cria um EventLog junto com uma fonte válida
EventLog.CreateEventSource(“Fonte2”, “TesteLog”);// Cria uma segunda fonte aproveitando o EventLog já criado
Você pode perceber que o EventLog contêm uma inteligência dentro da criação do evento, ou seja, caso o evento já tenha sido criado ele aproveita este evento e adiciona uma fonte nele. Outra item interessante é que, quando você cria um EventLog automaticamente ele cria um uma fonte de mesmo nome. Isto garante que o EventLog poderá ser usado.
Quando você cria uma nova fonte, você deve verificar se esta fonte já não esta registrada no computador, se já estiver registrada sua aplicação irá disparar uma exceção. Segue abaixo alguns recursos interessante que o EventLog tem para você evitar este problema e outros.
EventLog.Exists(“TesteLog”); //Verifica se existe um EventLog
EventLog.SourceExists(“TesteLog”); //verifica se existe uma fonte
EventLog.Delete(“TesteLog”); //Apaga o EventLog junto com todas as suas fontes
EventLog.DeleteEventSource(“Fonte”); //Exclui a fonte do evento
EventLog.CreateEventSource(“Fonte”, “TesteLog”);// Cria um EventLog junto com uma fonte válida
Você pode retornar uma lista de EventLogs cadastrados no computador utilizando o método GetEventLogs. Segue abaixo um exemplo.
foreach (EventLog e in EventLog.GetEventLogs())
{
Console.WriteLine(e.Log); //Nome do Log
Console.WriteLine(e.Source); //Recupera o nome da fonte ou registra quando você der uma entrada no log
Console.WriteLine(e.LogDisplayName); //Nome de visualização
Console.WriteLine(e.MachineName); //Maquina a qual o log esta referenciado
Console.WriteLine(e.OverflowAction.ToString()); //Retorna qual será o comportamento do log caso ele fique cheio
}
Enfim vamos aprender como registrar um evento que aconteceu na sua aplicação.
EventLog log = new EventLog(“TesteLog”); //Configura qual EventLog será utilizado para gravar o evento
log.Source = “Fonte”; //Configura em qual fonte será a entrada do evento.
log.WriteEntry(“Erro da aplicação”, EventLogEntryType.Error, 1001, 1); //Efetiva as configurações anteriores e grava o evento
Perceba que o método WriteEntry aceita quatro parâmetros de entrada. O primeiro é a descrição do evento, o segundo é o tipo do evento, que pode ser de Erro, Informação, Alerta, Sucesso ou Falha em uma auditoria de segurança, já os últimos dois são utilizados para identificar mais fácil o evento, Id do Evento e Id da Categoria. É muito útil caso você crie uma documentação do seu sistema. Porém você pode usar outros métodos WriteEntry que contêm menos parâmetros.
Para ler os eventos registrados em um determinado EventLog utilize o seguinte exemplo.
EventLog log = new EventLog(“TesteLog”); //Recupera um log existente
foreach (EventLogEntry entrada in log.Entries)
{
Console.WriteLine(entrada.Message); //Descrição do evento ocorrido
Console.WriteLine(entrada.Category); //Categoria do evento
Console.WriteLine(entrada.CategoryNumber); //Número da categoria do evento
Console.WriteLine(entrada.EntryType.ToString()); //Tipo do evento
Console.WriteLine(entrada.Index); //Índice do evento em relação a lista
Console.WriteLine(entrada.MachineName); //Maquina a qual o evento foi geral
Console.WriteLine(entrada.Source); //Fonte do evento
Console.WriteLine(entrada.TimeWritten); //Qual horário o evento foi escrito
Console.WriteLine(entrada.TimeGenerated); //Qual horário o evento foi gerado
Console.WriteLine(entrada.UserName); //Qual usuário estava logado durante o evento
}
E finalmente, para limpar o EventLog utilize o método Clear.
EventLog log = new EventLog(“TesteLog”); //Recupera um log existente
log.Clear(); //Limpa todas as entradas de um EventLog específico
Espero que este post tenha sido simples e prático e que possa ajudar você a instrumentar sua aplicação.
Usando coleções no .NET
Muitas vezes nós usamos somente coleção genérica do tipo List para trabalhar com coleções, no entanto o Framework.NET tem uma coleção específica para cada caso desde pilha e fila que aprendemos na faculdade até ordenação automática. Vamos logo ao assunto aprender para que serve cada uma e como implementar.
Coleções
ArrayList
O ArrayList é uma simples coleção que você pode adicionar qualquer tipo de objeto e remover, navegar, ordenar entre outras funções básicas. Adicione uma referencia a classe System.Collections no seu fonte e veja abaixo um exemplo de sua implementação.
ArrayList lista = new ArrayList(); //Instancia um ArrayList
lista.Add(“Teste 1”); //Adiciona uma string “Teste 1”
lista.Add(“Teste 2”); //Adiciona uma string “Teste 2”
lista.Add(5); //Adiciona uma int.
lista.Add(new Byte()); //Adiciona um Byte
lista.Remove(“Teste 2”); //Remove uma string “Teste 2” da lista
lista.Insert(2, “Teste 3”); //Insere uma string “Teste 3” na posição 2 do array
lista.AddRange(new int[] { 1, 2, 3 }); //Adiciona um array que no caso é um array de int
lista.RemoveRange(1, 2); //Remove um range de itens. No exemplo são dois itens a partir do índice 1
lista.RemoveAt(0); //A partir de um determinado índice remove um item
Para listar todo o conteúdo que contem na sua lista utilize este exemplo.
foreach (object obj in lista)
{
Console.WriteLine(obj.ToString());
}
O ArrayList ainda possui alguns métodos interessantes que ajudam a manipular a lista.
lista.Sort(); //Ordena o itens usando a implementação do IComparable. Os itens devem ser do mesmo tipo
lista.Reverse(); //Ordena em ordem inversa os itens
Console.WriteLine(lista.BinarySearch(5)); //Retorna o índice de item pesquisado.
Console.WriteLine(lista.Contains(5)); //Verifica se lista contem um determinado objeto
StringCollection
Esta classe trabalha de forma semelhante a classe ArrayList, porém ela é especializada em strings e não contem alguns métodos de ordenação. Segue abaixo como instanciá-la.
StringCollection listaString = new StringCollection();
Queue – Fila
Coleção que funciona como uma “Fila”, isto é, primeiro elemento que entra na coleção é o primeiro que sai. Muito útil em situações que você precisa de uma ordem de processamento por item.
Queue fila = new Queue(); //Instancia a fila
fila.Enqueue(“Teste 1”); //Adiciona um item na fila
fila.Enqueue(“Teste 2”); //Adiciona um item na fila
Console.WriteLine(fila.Dequeue()); //Retorna e remove automaticamente o primeiro item inserido na fila
fila.Peek(); //Retorna o primeiro item inserido na fila sem remove-lo da lista
Stack – Pilha
Coleção que funciona como uma “Pilha”, isto é, primeiro elemento que entra na coleção é o ultimo que sai.
Stack pilha = new Stack(); //Instancia a pilha
pilha.Push(“Teste 1”); //Adiciona um item na pilha
pilha.Push(“Teste 2”); //Adiciona um item na pilha
Console.WriteLine(pilha.Pop()); //Retorna e remove automaticamente o último elemento inserido da pilha
pilha.Peek(); //Retorna o último elemento inserido na pilha sem remove-lo da lista
Dicionários
Os dicionários funcionam de modo semelhante as coleções com chaves que poderão ser usadas como índices. Estas chaves facilitam, e muito, a pesquisa de itens na lista.
SortedList
Esta classe é uma lista que se auto-ordena a medida que você vai inserindo os itens de acordo acordo com a chave que você esta passando como parâmetro.
SortedList listaOrdenada = new SortedList(); //Instancia a classe
listaOrdenada.Add(“Chave 2”, “Teste 1”); //Adiciona um item com a chave “Chave 2” e valor “Teste 1”
listaOrdenada.Add(“Chave 3”, “Teste 2”); //Adiciona um item com a chave “Chave 3” e valor “Teste 2”
listaOrdenada.Add(“Chave 1”, “Teste 3”); //Adiciona um item com a chave “Chave 1” e valor “Teste 3”
Para recuperar os itens da lista e verificar qual a ordem que o conteúdo esta na lista utilize o seguinte código.
foreach (DictionaryEntry item in listaOrdenada)
{
Console.WriteLine(item.Value);
}
Como todo dicionário você poderá recuperar um item específico de acordo com a chave.
Console.WriteLine(listaOrdenada[“Chave 2”]);
Segue alguns métodos que são úteis para a manipulação da SortedList
Console.WriteLine(listaOrdenada.ContainsKey(“Chave 2”)); // Verifica se uma determinada chave existe na lista
Console.WriteLine(listaOrdenada.ContainsValue(“Teste 3”)); //Verifica se um determinado valor existe na lista
Console.WriteLine(listaOrdenada.GetByIndex(0)); //Retorna o valor de um determinado índice da lista
Console.WriteLine(listaOrdenada.GetKey(0)); //Retorna a chave de acordo com o índice
Console.WriteLine(listaOrdenada.IndexOfValue(“Teste 3”)); //Retorna o índice de acordo com o valor
Console.WriteLine(listaOrdenada.IndexOfKey(“Chave 2”)); //Retorna a índice de acordo com a chave
NameValueCollection
Semelhante a SortedList com algumas diferenças como: Especializada em strings, não possui ordenação automática, pode ser acessada tanto pela chave quanto por índice e você pode adicionar mais de um valor com a mesma chave.
NameValueCollection nomeValorLista = new NameValueCollection(); //Instancia a Classe
nomeValorLista.Add(“Nome 1”, “Valor 1”); //Adiciona um item
Console.WriteLine(nomeValorLista[0]); //Recupera um valor de acordo com o índice
Console.WriteLine(nomeValorLista[“Nome 1”]); //Recupera um valor de acordo com a chave
StringDictionary
StringDictionary é um dicionário especializado em strings que armazena seus valores em uma tabela hash. Utiliza o namespace System.Collections.Specialized e a forma de implementar é muito semelhante ao SortedList por isso vou demonstrar apenas como se instancia a classe.
StringDictionary stringDic = new StringDictionary();
ListDictionary
Semelhante ao StringDictionary mas não é especializado em strings e ela foi projetada para ter performance quando utilizar quando você manipular menos de 10 itens.
ListDictionary listDic = new ListDictionary();
HashTable
Semelhante ao ListDictionary porém com a recomendação de utiliza-la quando você manipular acima de 10 itens.
Hashtable hash = new Hashtable();
HybridDictionary
Como o próprio nome nos sugestiona, esta classe trabalha de forma hibrida. Ela funciona como um ListDictionary quando você tem menos de 10 itens e como um HashTable quando você passa desta quantidade.
HybridDictionary hybriDic = new HybridDictionary();
Coleções genéricas
Na minha opinião as coleções genéricas são uma nova versão das antigas coleções e dicionários. Você percebeu que, com exceção das coleções especializadas em strings, todos os itens que eu expliquei aceitam qualquer tipo de objeto dentro da lista, isto pode causar exceções na hora da conversão, queda de performance devido ao boxing e unboxing e não queda na produtividade devido a não saber qual tipo de objeto irá retornar.
Para acabar com este problema, criou-se as coleções genéricas, que recebem um parâmetro informando qual tipo de objeto irá trabalhar dentro da lista.
Segue abaixo uma tabela de substituição.
Classe Genérica | Classe não genérica |
List<T> | ArrayList, StringCollection |
Dictionary<T,U> | HashTable, ListDictionary, HybridDictionary, OrderedDictionary, NameValueCollection, StringDictionary |
Queue<T> | Queue |
Stack<T> | Stack |
SortedList<T,U> | SortedList |
Collection<T> | CollectionBase |
ReadOnlyCollection<T> | ReadOnlyCollectionBase |
List<T>
Para instanciar uma List<T> do tipo genérico, você deve passar como parâmetro a classe definindo que a coleção será um determinado tipo e adicionar o namespace System.Collections.Generic. No exemplo abaixo estou dizendo que a minha lista irá apenas elementos do tipo int.
List<int> list = new List<int>();
Você irá perceber que, diferente das coleções explicadas anteriormente, esta lista irá apenas aceitar valores do tipo int e também só irá retornar valores deste mesmo tipo.
list.Add(1); //O método Add agora só aceita int como parâmetro.
E na hora de recuperar os valores eu posso esperar o tipo correto.
foreach (int numero in list)
{
Console.WriteLine(numero.ToString());
}
Outra característica muito útil que as coleções genéricas têm é possibilidade de usar suas próprias classes para definir o tipo da lista.
List<Pessoa> list = new List<Pessoa>(); //No caso estou indicando para que seja criada uma lista da classe Pessoa.
SortedList<T,U>
Funciona da mesma forma que a SortedList não genérica que mantem sempre a coleção ordenada, porem você pode configurar o tipo de chave e valor que você irá utilizar. Segue abaixo um exemplo de implementação.
SortedList<int, Pessoa> listaOrdenada = new SortedList<int, Pessoa>(); //Instancio indicando que a chave será um int e o valor Pessoa
listaOrdenada.Add(3, new Pessoa(“Teste1”)); //Adiciono a chave 3 e o valor Pessoa(“Teste1”)
listaOrdenada.Add(1, new Pessoa(“Teste2”)); //Adiciono a chave 3 e o valor Pessoa(“Teste1”)
listaOrdenada.Add(2, new Pessoa(“Teste3”)); //Adiciono a chave 3 e o valor Pessoa(“Teste1”)
foreach (Pessoa pessoa in listaOrdenada.Values)
{
Console.WriteLine(pessoa.ToString()); //Retorna o valor na ordem correta de acordo com a chave
}
Queue<T> – Fila genérica
Funciona da mesma forma que a classe Queue não genérica, porém você pode configurar qual o tipo de objeto estará dentro da sua lista. Segue abaixo como instancia-la.
Queue<Pessoa> fila = new Queue<Pessoa>();
Stack<T> – Pilha genérica
De semelhante modo.
Stack<Pessoa> pilha = new Stack<Pessoa>();
Dictionary<T,U>
O Dictionary genérico substitui todos dicionários demonstrados anteriormente.
Dictionary<int, Pessoa> dicGenerico = new Dictionary<int, Pessoa>(); //Instancio o dicionário com a chave do tipo int e valor do tipo Pessoa
Manipulando arquivos no .NET
Vou mostrar como manipular arquivos dentro de sua aplicação. Irei escrever no artigo duas formas de fazer isso. A primeira é usando a classe File do namespace System.IO que é a maneira mais fácil de fazer isso e funciona muito bem para a maioria das situações. Agora se você precisa tratar arquivos grandes, compactação, arquivos em memória, criptografia e etc… utilize a segunda parte do artigo aonde eu apresento a classe Stream também do namespace System.IO.
File
Com classe File você pode ler arquivos, escrever, criptografar(de acordo com Windows), alterar alguns atributos e criar backups. Vamos direto aos exemplos com os métodos mais utilizados.
File.WriteAllBytes(“teste.txt”, new byte[] { byte.MaxValue }); //Sobrescreve um array de bytes no novo arquivo ou existente
File.WriteAllLines(“teste.txt”, new String[] { “Teste 1”, “Teste 2” }); //Sobrescreve o texto (array string) no novo arquivo ou existente
File.WriteAllText(“teste.txt”, “conteúdo total”); //Sobrescreve o texto no novo arquivo ou existente
File.AppendAllText(“teste.txt”, “conteúdo”); //Adiciona um texto ao arquivo existente ou cria um novo
File.Encrypt(“teste.txt”); //Criptografa o arquivo de acordo com as configurações do Windows
File.Decrypt(“teste.txt”); //Descriptografa o arquivo de acordo com as configurações do Windows
byte[] allBytes = File.ReadAllBytes(“teste.txt”); //Recupera todos os bytes do conteúdo do arquivo
string[] allLines = File.ReadAllLines(“teste.txt”); //Recupera um array de string contento todas as linhas do arquivo
string allText = File.ReadAllText(“teste.txt”); //Recupera todo o conteúdo do arquivo para dentro de uma string
File.SetAttributes(“teste.txt”, FileAttributes.Normal); //Configura o atributo do arquivo. Ex: Normal, Leitura, Sistema
File.SetCreationTime(“teste.txt”, DateTime.Now); //Configura a data de criação do arquivo
File.SetLastAccessTime(“teste.txt”, DateTime.Now); //Configura a ultima data que o arquivo foi acessado
File.SetLastWriteTime(“teste.txt”, DateTime.Now); //Configura a ultima data que o arquivo foi escrito
File.Replace(“teste.txt”, “teste1.txt”, “testebkp.txt”); //Substitui um arquivo pelo outro e faz uma cópia de segurança do segundo arquivo
Lembrando que a classe File trabalha diretamente com o arquivo, ou seja, cada a vez que você precisar escrever ou ler alguma coisa você precisa-rá chamar o arquivo novamente e executar a tarefa. Outro item importante é que a classe File trabalha sempre com o arquivo todo, por ex: nos métodos de Read o arquivo todo é lido de uma vez.
Stream
Utilizar a classe Stream é um pouco mais complexo, porém você terá maior controle sobre o arquivo. Stream é a classe base que permite você trabalhar diretamente com dados(Arquivo, Memória, Arquivo compactado e etc..) de forma seqüencial ou randômica. (Diferente do File que trabalha com o arquivo inteiro de uma vez).
Segue abaixo um diagrama demonstrando o diagrama de classe que representa a classe Stream e as classes que a implementam.
Stream : Contém propriedades e métodos que serão usadas por todas as classes que a implementam. (Exemplo de métodos e propriedades compartilhada por todos os Streams: CanRead, Write, Position).
FileStream : Classe especializada no tratamento de arquivos.
BufferedStream : Classe especializada em prover performance por buffer de leitura e de escrita.
MemoryStream : Classe que permite você criar Streams diretamente na memoria.
DeflateStream : DeflateStream permite você compactar Streams utilizando o método de compressão Deflate.
GZipStream : GZipStream é semelhante ao DeflateStream porém com o método de compressão GZip.
UnmanagedMemoryStream : Fornece acesso a blocos não-gerenciados de memória.
Vamos explorar a classe base Stream. Todas as outras classes de Stream são baseadas nesta classe, isso quer dizer que, todos os métodos aqui serão utilizados pelas outras classes. Porém cada um dos outros Streams serão especializados em suas funções como descrevi acima. Como é uma classe abstrata, para instancia-la eu preciso determinar qual classe irá implementá-la. No caso estou usando um MemoryStream somente para exemplificar, no entanto você pode usar qualquer Stream para isso.
Stream _stream = new MemoryStream();
Segue abaixo algumas propriedades interessantes para você testar.
Console.WriteLine(_stream.CanRead); // Verifica se o Stream pode ser lido
Console.WriteLine(_stream.CanSeek); // Verifica se pode haver uma busca
Console.WriteLine(_stream.CanTimeout); // Verifica se o Stream tem um time-out
Console.WriteLine(_stream.CanWrite); // Verifica se o Stream pode ser lido
Console.WriteLine(_stream.Length); // Retorna o tamanho do Stream
Console.WriteLine(_stream.Position); //Recupera a posição do ponteiro dentro da memória
Para escrever e ler em um Stream você deve usar os métodos Write/WriteByte e Read/ReadByte. É muito importante entender o conceito do ponteiro, toda a escrita e leitura é feita a partir da posição que o ponteiro esta no momento. Segue abaixo um exemplo completo de escrita e leitura um Stream.
byte[] bytes = new byte[2] { byte.MaxValue, byte.MaxValue };
_stream.Position = 0; // Configura o ponteiro na posição 0
_stream.WriteByte(255); // Escreve o byte 255 (equivalente a ff) e passa o ponteiro para o fim da escrita
_stream.Write(bytes, 0, bytes.Length); // Recebe um array de bytes para escrever, 0 significa que não terá buffer, e o terceiro parâmetro indica quantos bytes devem ser escritos
_stream.Position = 0; //Retorna o ponteiro para posição inicial
while (_stream.Position != _stream.Length) //Executa o while enquanto o ponteiro não chegar no final
{
Console.WriteLine( _stream.ReadByte()); //Le o byte e avança uma posição no ponteiro
}
_stream.Position = 0; // Configura o ponteiro na posição 0
Console.WriteLine(_stream.Read(bytes, 0, bytes.Length));// Recebe um array de bytes para ler, 0 significa que não terá buffer, e o terceiro parâmetro indica quantos bytes devem ser lidos
Abaixo irei explicar como se inicializa os Streams mais utilizados.
FileStream : Para inicializar o você precisa indicar o path do arquivo, como será o modo de utilização do arquivo, e qual tipo de permissão ele terá.
FileStream _fileStream = new FileStream(@”C:\Teste.txt”, FileMode.Open, FileAccess.Read); //Neste exemplo eu estou indicando que o arquivo C;\Teste.txt existe fisicamente e eu irei abri-lo para utilizar, e a permissão necessária para utilização do mesmo será somente para leitura.
Podemos inicializar o FileStream direto da classe File. Segue abaixo alguns exemplos.
FileStream fileStream = File.Create(“teste.txt”); // Cria um arquivo e associa ao FileStream
FileStream fileStream = File.Open(“teste.txt”, FileMode.Open); // Abre um arquivo e associa ao FileStream
FileStream fileStream = File.OpenRead(“teste.txt”); // Abre um arquivo e associa ao FileStream permitindo a leitura
FileStream fileStream = File.OpenWrite(“teste.txt”); // Abre um arquivo e associa ao FileStream permitindo a escrita
MemoryStream : Como o MemoryStream é baseado apenas na memória sua inicialização é muito simples.
MemoryStream _memoryStream = new MemoryStream();
GzipStream : O Stream de compactação GzipStream precisa que você indique se o processo vai ser de compactação ou de descompactação e também indique qual Stream vai ser compactado ou descompactado.
GZipStream _gZipStream = new GZipStream(_fileStream, CompressionMode.Compress); //Neste exemplo estou indicando que vou compactar um FileStream
Leitura e escrita
Você pode usar o exemplo de escrita e leitura da classe base Stream que eu demonstrei para todas as classes que implementam a classe Stream, porém existe algumas classes especializadas para executar isso. Segue abaixo um diagrama que demonstra toda a estrutura completa de classes para leitura e escrita em um Stream.
TextWriter : Classe base para todas classes de escrita com exceção da BinaryWriter.
StreamWriter : Classe especializada na escrita em Stream.
HtmlTextWriter : Classe especializada na escrita de Html.
StringWriter : Classe especializada na escrita de strings.
BinaryWriter : Classe especializada na escrita de bytes.
TextReader : Classe base para todas classes de leitura com exceção da BinaryReader.
StreamReader : Classe especializada na leitura em Stream.
StringReader : Classe especializada na leitura de strings.
BinaryReader : Classe especializada na leitura de bytes.
StreamWriter
A classe StreamWriter herda as funcionalidade da classe TextWriter. Ela permite você escrever no Stream. Abaixo segue um exemplo de como utilizar.
StreamWriter sw = new StreamWriter(_fileStream); //Inicializa um StreamWriter. Utilizei um FileStream que aponta para um arquivo físico
sw.Write(“teste”); //Escreve no StreamWriter uma string que você passar como parâmetro
sw.WriteLine(); //Escreve uma quebra de linha no StreamWriter
sw.Flush(); // Escreve no Stream configurado (no caso um FileStream representando um arquivo) e limpa o buffer do StreamWriter
sw.AutoFlush = false; // Você pode utilizar o auto flush para indicar o flush será executado automaticamente cada vez que você chamar o método Write ou WriteLine do StreamWriter
Podemos inicializar o StreamWriter diretamente da classe estática File.
StreamWriter sw = File.CreateText(“teste.txt”); //Cria ou abre um arquivo físico e associa com o StreamWriter
StreamWriter sw = File.AppendText(“teste.txt”); //Associa um arquivo existente e adiciona o conteúdo que será escrito no StreamWriter com o conteúdo do arquivo
StreamReader
StreamReader é a classe responsável por ler conteúdo dos Stream e herda da classe TextReader. Abaixo eu demonstrarei com usá-la.
StreamReader sr = new StreamReader(_fileStream); //Inicializa o StreamReader. Utilizei um FileStream que aponta para um arquivo físico
Console.WriteLine(sr.ReadLine()); //Le a linha em que o ponteiro esta no momento e adianta o ponteiro para a próxima linha
Console.WriteLine(sr.ReadToEnd()); //Le todo o FileStream a partir do posição atual do ponteiro a avança o ponteiro para o final
Console.WriteLine(sr.EndOfStream); //Verifica se o ponteiro está no final do FileStream
Liberando recursos
É uma boa prática chamar o mesmo close para liberar recursos do sistema quando estamos trabalhando com memória não-gerenciável como por exemplo Streams, Writer e Reader.
_fileStream.Close();
sw.Close();
sr.Close();
E chegamos ao fim de mais um artigo. Como este assunto é muito vasto, decidi escolher os itens mais comuns de serem utilizados para demonstrar aqui, então por isso que algumas classes eu não descrevi como implementa-las.
Espero que tenham gostado.
Monitorando Pastas
Quantas vezes precisamos implementar um looping para verificar se um determinado arquivo foi copiado para uma pasta, ou quanta vezes temos que monitorar a alteração do conteúdo de um documento ou se o mesmo foi apagado. Este processo exige um esforço muito grande além de diminuir a performance de nosso sistema devido a quantidade de loopings que temos que fazer.
Para resolver este problema podemos utilizar a classe FileSystemWatcher do namespace System.IO. Com ela nós podemos configurar uma determinada pasta para ser monitorada e quando algum evento acontecer nós seremos avisados.
Para iniciar o monitoramento você só precisa configurar duas coisas.
FileSystemWatcher monitorPastas = new FileSystemWatcher(@”C:\Teste”); //Instancia a classe configurando a pasta que será monitorada
monitorPastas.EnableRaisingEvents = true; //Habilita o monitoramento
Você também pode fazer alguma configurações interessantes
monitorPastas.Filter = “*.txt”; //Nesta string você pode filtrar quais arquivos você deseja monitorar
monitorPastas.IncludeSubdirectories = true; //Booleano indicando se subdiretórios serão monitorados ou não
monitorPastas.Path = @”C:\Teste”; //Altera o path que será monitorado
monitorPastas.NotifyFilter = NotifyFilters.FileName | NotifyFilters.Size; //Configura quais tipos de alterações deverão ser monitoradas
Para receber as notificações do monitoramento e trata-los você deve associar um evento específico a um método criado por você respeitando as assinatura do delegate.
Segue abaixo os quatros eventos possíveis que você pode monitorar.
monitorPastas.Changed += new FileSystemEventHandler(monitorPastas_Changed); //Alterações
monitorPastas.Created += new FileSystemEventHandler(monitorPastas_Created); //Criações
monitorPastas.Deleted += new FileSystemEventHandler(monitorPastas_Deleted); //Exclusões
monitorPastas.Renamed += new RenamedEventHandler(monitorPastas_Renamed); //Renomeações
Segue abaixo os quatros métodos que você deve criar para poder tratar esses eventos.
void monitorPastas_Renamed(object sender, RenamedEventArgs e)
{
Console.WriteLine(e.ChangeType + ” de ” + e.OldFullPath + ” para ” + e.Name);
}
void monitorPastas_Deleted(object sender, FileSystemEventArgs e)
{
Console.WriteLine(e.ChangeType + “: ” + e.FullPath);
}
void monitorPastas_Created(object sender, FileSystemEventArgs e)
{
Console.WriteLine(e.ChangeType + “: ” + e.FullPath);
}
void monitorPastas_Changed(object sender, FileSystemEventArgs e)
{
Console.WriteLine(e.ChangeType + “: ” + e.FullPath);
}
Espero que com essa dica vocês tenham a possibilidade de melhorar a performance dos seus sistemas.
Manipulando Path
O .NET Framework nos disponibiliza uma classe pouco utilizada por não ser conhecida, porém muito útil para tratarmos path, nome de arquivos, extensões e etc..
Dentro no namespace System.IO existe uma classe estática chamada Path. Esta classe acaba com aquele quantidade de loopings que fazíamos toda vez que precisamos verificar um extensão de arquivo ou então os replaces para retirar os caráteres separados de um path. A classe permite manipular um path de maneira muito simples e útil. Sem mais delongas segue abaixo alguns exemplos com seus devidos comentários.
Console.WriteLine(Path.ChangeExtension(@”C:\Teste.ttt”, “txt”)); //Altera a extensão do arquivo no path
Console.WriteLine(Path.Combine(@”C:\Teste”, @”SubTeste\sad.txt”)); //Concatena dois path
Console.WriteLine(Path.DirectorySeparatorChar); //Retorna o caracter separador de diretórios
Console.WriteLine(Path.GetDirectoryName(@”C:\Windows\win.ini”)); //Retorna o diretório do path
Console.WriteLine(Path.GetExtension(@”C:\Windows\win.ini”)); //Retorna a extensão do arquivo
Console.WriteLine(Path.GetFileName(@”C:\Windows\win.ini”)); //Retorna o nome completo do arquivo
Console.WriteLine(Path.GetFileNameWithoutExtension(@”C:\Windows\win.ini”) ); //Retorna o nome do arquivo sem extensão
Console.WriteLine(Path.GetFullPath(@”win.ini”)); //Retorna o path completo do arquivo
Console.WriteLine(Path.GetInvalidFileNameChars()); //Disponibiliza todos caracteres inválidos para criar um nome de arquivo
Console.WriteLine(Path.GetInvalidPathChars()); //Disponibiliza todos os caracteres inválido para criar uma pasta
Console.WriteLine(Path.GetPathRoot(@”C:\Windows\win.ini”)); //Retorna o drive do path
Console.WriteLine(Path.GetRandomFileName()); //Gera um nome de arquivo randômico
Console.WriteLine(Path.GetTempFileName()); //Retorna um nome de arquivo randômico dentro da pasta temporária
Console.WriteLine(Path.GetTempPath()); //Retorna o path completo da pasta temporária
Console.WriteLine(Path.HasExtension(@”C:\Windows\win.ini”)); //Verifica se o arquivo do path tem extensão
Console.WriteLine(Path.VolumeSeparatorChar); //Retorna o caracter separador de volumes
Espero que com esta dica o seu trabalho com path seja facilitado.
Sistemas de Arquivo no .NET
O .Net Framework tem classes que nos permitem criar, deletar, navegar, e etc.. arquivos e pastas do Windows. Podemos até criar um Windows Explorer Genérico com todas essas funcionalidades e outras mais.
Todas essas features ficam dentro do namespace System.IO. Abaixo explicarei algumas das possiblidades que o .Net disponibiliza para manipulação de sistema de arquivos.
Manipulando Drives
A classe DriveInfo representa um drive (ex: C:\, CDROM, USB, etc.). Para instanciá-la a partir de um drive existente basta você passar o nome do drive no construtor padrão da classe.
DriveInfo drive = new DriveInfo(@"C:\");
Dentro do objeto drive que instanciamos no exemplo anterior temos alguns métodos e propriedades interessantes para ser usadas. Segue abaixo alguns exemplos:
drive.Name; //Retorna o nome do Drive
drive.AvailableFreeSpace; //Total de espaço disponível
drive.DriveFormat; //Se ele é NTFS ou FAT
drive.DriveType; //Se ele é CDROM, HD, USB e etc..
drive.TotalSize; //Tamanho total
Para retornar todos os drives do computador utilize o seguinte exemplo:
foreach (DriveInfo drive in DriveInfo.GetDrives())
{
Console.WriteLine(drive.Name);
}
Pastas ou Diretórios
De modo semelhante aos drives, você pode utilizar a classe DirectoryInfo para manipular pastas ou diretórios. Para instanciar utilize:
DirectoryInfo diretorio = new DirectoryInfo(@"C:\"); ou DirectoryInfo diretorio = new DirectoryInfo(@"C:\MinhaPasta");
Segue abaixo alguns métodos e propriedades úteis:
diretorio.Name; //Retorna o nome da Pasta
diretorio.CreationTime.ToShortDateString(); //Data de criação
diretorio.Exists; //Verifica se o diretório existe fisicamente
diretorio.FullName; //Path completo
diretorio.Create();//Cria o diretório fisicamente
diretorio.Delete();//Apaga o diretório
diretorio.MoveTo(@"C:\NovoDiretorio\"); //Move o diretório para outro path
Para retornar todos os sub-diretórios do objeto diretório utilize:
foreach (DirectoryInfo subdiretorio in diretorio.GetDirectories())
{
Console.WriteLine(subdiretorio.Name);
}
Para retornar os arquivos de sistema:
foreach (FileSystemInfo arquivoSistema in diretorio.GetFileSystemInfos())
{
Console.WriteLine(arquivoSistema.Name);
}
E para retornar todos os arquivos:
foreach (FileInfo arquivo in diretorio.GetFiles())
{
Console.WriteLine(arquivo.Name);
}
Pastas ou Diretórios 2
Existe também a classe Diretory, que trabalha de forma estática e de modo muito semelhante a classe DirectoryInfo. Segue abaixo algumas métodos úteis da classe directory para você obter informações.
Console.WriteLine(Directory.Exists(@"C:\Teste")); //Verifica se o diretório existe fisicamente
Console.WriteLine(Directory.GetCreationTime(@"C:\Teste")); //Retorna a data de criação do diretório
Console.WriteLine(Directory.GetCurrentDirectory()); //Retorna o diretório atual da aplicação
Para manipulação, a classe contem os seguintes métodos.
Directory.CreateDirectory(@"C:\Teste"); //Cria um diretório
Directory.Move(@"C:\Teste", @"C:\Teste2"); //Move o diretório
Directory.Delete(@"C:\Teste2"); //Delete a o diretório
Com a classe Directory você pode retornar um array de strings contendo todos os arquivos ou diretórios contido dentro de um path.
//Retorna todos os diretórios
foreach (string diretorio in Directory.GetDirectories(@"C:\"))
{
Console.WriteLine(diretorio);
}
//Retorna todos os arquivos
foreach (string arquivo in Directory.GetFiles(@"C:\"))
{
Console.WriteLine(arquivo);
}
Arquivos
Para manipular arquivos é da mesma forma, porem utilizando a classe FileInfo:
FileInfo arquivo = new FileInfo("teste.txt"); //Por Default a pasta padrão é ~\bin\debug
FileInfo arquivo = new FileInfo(@"C:\teste.txt"); //O construtor também aceita o path completo
Segue abaixo alguns métodos úteis:
Console.WriteLine(arquivo.Name); //Retorna o nome da Pasta
Console.WriteLine(arquivo.CreationTime.ToShortDateString()); //Data de criação
Console.WriteLine(arquivo.Exists); //Verifica se o diretório existe fisicamente
Console.WriteLine(arquivo.FullName); //Path completo
Também é possível manipular o arquivo dentro do FileInfo
arquivo.Create(); //Cria um arquivo físico baseado no FileInfo
arquivo.CreateText(); //Cria um StreamWriter e um novo arquivo para ser escrito
arquivo.CopyTo(@"c:\teste2.txt"); // Cria uma cópia do arquivo físico
arquivo.MoveTo(@"c:\teste2.txt"); // Move o arquivo físico para um novo arquivo
arquivo.Delete(); // Deleta o arquivo físico
Arquivos2
Outra forma de você manipular arquivos é utilizando a classe File. Ela tem funcionalidade muito semelhantes a classe FileInfo, porém trabalha de forma estática, ou seja, sem precisar instanciar o objeto.
File.Create(@"C:\teste.txt"); //Cria um arquivo físico
File.CreateText(@"C:\teste.txt"); //Cria um StreamWriter e um novo arquivo para ser escrito
File.Copy(@"C:\teste.txt", @"C:\teste2.txt"); // Cria uma cópia do arquivo físico
File.Move(@"C:\teste.txt", @"C:\teste3.txt"); // Move o arquivo físico para um novo arquivo
File.Delete(@"C:\teste3.txt"); //Deleta o arquivo físico
Console.WriteLine(File.Exists(@"C:\teste2.txt")); //Verifica se o arquivo existe fisicamente
E por enquanto é isso pessoal. Escreverei mais sobre sistemas de arquivo no .Net no meu próximo post.
Enviando E-mail com o .NET
Hoje vou escrever sobre como criar e enviar um e-mail utilizando o .NET. Vamos direto ao assunto.
Criando um objeto MailMessage
No .NET você precisa instanciar a classe MailMessage para criar o seu e-mail. Para instanciá-la você deve incluir o namespace System.Net.Mail que provê uma série de features para este contexto.
No exemplo abaixo você verá como instanciar a classe MailMessage já configurando o remetente, destinatário, Assunto e o conteúdo.
MailMessage email = new MailMessage(remente@servidor.com, destinatario@servidor.com, “Assunto”, “Conteúdo”);
Você também pode utilizar o construtor padrão e configurar as propriedades posteriormente. Perceba que eu utilizei a classe MailAdress para adicionar o destinatário e o remetente.
MailMessage email = new MailMessage();
email.From = new MailAddress(“remente@servidor.com“, “Remetente”);
email.To.Add(new MailAddress(“destinatario@servidor.com“, “Destinatário”));
email.Subject = “Assunto”;
email.Body = “Conteúdo”;
email.Priority = MailPriority.Normal;
Anexando arquivos ao e-mail
Para anexar um arquivo qualquer no seu e-mail o seu objeto de e-mail criado e adicione um objeto “Attachment” contento o caminho para o arquivo.
email.Attachments.Add(new Attachment(@”C:\Pasta\arquivo.txt”));
Você também pode utilizar as configurações do “Attachment” para anexar arquivos do tipo MIME direto da memória. mas para isso você precisa ter carregado um objeto “Stream” contendo o seu arquivo.
Stream arq= new FileStream(@”C:\ebook.pdf”, FileMode.Open, FileAccess.Read);
email.Attachments.Add(new Attachment(arq, “livro.pdf”, MediaTypeNames.Application.Pdf));
E-mail com formato HTML
A classe MailMessage também permite que você crie o corpo do e-mail como html ao invés de um simples texto. Segue abaixo o exemplo.
email.Body = “<html><body>Este conteúdo é um html</body></html>”;
email.IsBodyHtml = true;
Alguns leitores de e-mail podem não ler e-mails quando o corpo do mesmo está configurado como html. Para isso você utiliza a classe AlternateView. Basta você criar uma visualização html e uma para um texto simples. Abaixo eu demonstro como fazer isso.
AlternateView visualizacaoHtml= AlternateView.CreateAlternateViewFromString(“Meu conteúdo html”, null, MediaTypeNames.Text.Html);
AlternateView visualizacaoTexto= AlternateView.CreateAlternateViewFromString(“Meu texto simples”, null, MediaTypeNames.Text.Plain);
email.AlternateViews.Add(visualizacaoHtml);
email.AlternateViews.Add(visualizacaoTexto);
E se você quiser incluir uma imagem dentro do conteúdo html que não seja um anexo utilize a classe LinkedResource. Preste atenção no src da imagem no conteúdo html e na propriedade ContentID do objeto LinkedResource.
AlternateView visualizacaoHtml = AlternateView.CreateAlternateViewFromString(“<html><body><img src=\”cid:Foto\”></body></html>”, null, MediaTypeNames.Text.Html);
LinkedResource recursoLinkado = new LinkedResource(“fotoSalva.jpg”, MediaTypeNames.Image.Jpeg);
recursoLinkado.ContentId = “Foto”;
visualizacaoHtml.LinkedResources.Add(recursoLinkado);
Enviando o e-mail criado
Para enviar o e-mail criado você irá utilizar uma classe chamada SmtpClient que também esta no namespace System.Net.Mail. Você apenas precisa ter o endereço do seu servidor SMTP, o resto o .Net fará pra você.
SmtpClient clienteSmtp = new SmtpClient(“smtp.servidor.com”);
clienteSmtp.Send(m);
Para habilitar envio com ssl utilize a propriedade EnableSsl.
clienteSmtp.EnableSsl = true;
Para configurar o envio de e-mail utilizando as credenciais default habilite a propriedade UseDefaultCredentials.
clienteSmtp.UseDefaultCredentials = true;
Para definir uma nova credencial adicione o namespace “System.Net” e configure a propriedade Credentials com um objeto NetworkCredential.
clienteSmtp.Credentials = new NetworkCredential(“usuário”, “senha”);
Você também pode utilizar a credencial que esta no cache do usuário.
clienteSmtp.Credentials = CredentialCache.DefaultNetworkCredentials;
Para configurar um TimeOut para o envio de e-mail utilize a propriedade Timeout.
clienteSmtp.Timeout = 10;
Enviando o e-mail de modo assíncrono
Como o envio de e-mail geralmente é um processo lento, pode acontecer de sua aplicação ficar esperando o envio para poder continuar o processamento. Podemos evitar isso utilizando o envio assíncrono. Para utilizar o envio assíncrono, você precisa associar um método (que tenha os parâmetros object e AsyncCompletedEventArgs) ao evento SendCompleted e enviar o e-mail utilizando o método SendAsync.
clienteSmtp.SendCompleted +=new SendCompletedEventHandler(EnvioCompleto);
clienteSmtp.SendAsync(m);
static void EnvioCompleto(object sender, AsyncCompletedEventArgs e)
{ }
Para cancelar o envio de e-mail assíncrono basta chamar o método SendAsyncCancel
clienteSmtp.SendAsyncCancel;
É isso ai pessoal, espero que este artigo ajude vocês implementarem o envio de e-mail em suas aplicações.
Francisco Gonçalves
Desvendando o Globalization do .NET
O .NET contem recursos que possibilitam que sua aplicação trabalhe de forma condizente com uma linguagem/cultura específica sem que você precise refazer o código quando você quiser mudar de cultura.
Abaixo eu descreverei várias funcionalidade do Globalization que podemos utilizar em nossos projetos. Recomendo ler os itens na seqüência, pois os assuntos estão organizados por pré-requisitos de aprendizado.
Verificando qual cultura sua aplicação está configurada
Por padrão sua aplicação assume as configurações de cultura que seu Windows está configurado. Para verificar isso utilize o código abaixo. Para utiliza-lá você precisa incluir o namespace System.Threading na sua aplicação.
Thread.CurrentThread.CurrentCulture.ToString();
No meu caso este exemplo retorna “pt-BR” aonde “pt”(Português) que define as configurações da linguagem e “BR” (Brasil) define as configurações regionais, específica de cada país.
No código abaixo você pode verificar a configuração que o Resource Manager esta configurado.
Thread.CurrentThread.CurrentUICulture.ToString();
Alterando a configurações regionalização da sua aplicação
Para alterar as configurações de sua aplicação, primeiro você precisa conhecer a Classe CultureInfo. É ela que Fornece informações sobre uma cultura específica. Ex.: Calendário, Ordenação e formatação de datas e horas. Para utiliza-lá você precisa incluir o namespace System.Globalization na sua aplicação.
Segue abaixo o código para realizar a alteração.
Thread.CurrentThread.CurrentCulture = new CultureInfo(“en-US”);
Neste caso estou alterando a minha aplicação para utilizar as configurações “en”(Inglês) “US(Estados Unidos)”. Você também pode alterar o CurrentUICulture da mesma forma.
Você pode verificar a diferença de formatação de cada cultura em relação a data utilizando o seguinte código para cada configuração que você escolher. Utilizando um projeto console.
Console.WriteLine(DateTime.Now.ToString());
Utilizando culturas diferentes no método ToString()
Para configurar a cultura específica apenas na saída string de uma propriedade utilize o código abaixo. Neste caso estou alterando para Inglês-Estados Unidos.
Console.WriteLine(DateTime.Now.ToString(new CultureInfo(“en-US”)));
Listando todas as culturas registradas no Framework
A classe CultureInfo tem um método chamado GetCultures que retorna um coleção de CultureInfo registrados no Framework. Este método aceita um CultureTypes como parâmetro que funciona como um filtro. Para retornar todas as culturas registradas utilize o seguinte código.
foreach (CultureInfo item in CultureInfo.GetCultures(CultureTypes.AllCultures))
Console.WriteLine(item.ToString());
Recuperando as configurações de uma cultura específica
A classe CultureInfo possui uma série propriedades que permitem você recuperar informações interessantes sobre uma configuração. São elas a NumberFormat e a DateTimeFormat. Dentro de cada uma delas você tem configurações específicas para número e Data. Por exemplo: NumberDecimalSeparator e DateSeparator.
Abaixo um exemplo de utilização.
CultureInfo cultura = new CultureInfo(“pt-BR”);
cultura.NumberFormat.NumberDecimalSeparator;
Construindo e registrando sua própria cultura
Você pode construir culturas personalizadas para ser usadas por todas suas aplicações.
Primeiro adicione o assembly sysglobl.dll (que na minha maquina fica na pasta C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\sysglobl.dll).
Utilize a classe CultureAndRegionInfoBuilder para construir a nova cultura.
CultureAndRegionInfoBuilder construtorCultura = new CultureAndRegionInfoBuilder(“pt-K1”, CultureAndRegionModifiers.None);
Carregue sua nova cultura baseada em outra.
construtorCultura.LoadDataFromCultureInfo(new CultureInfo(“pt-BR”)); construtorCultura.LoadDataFromRegionInfo(new RegionInfo(“BR”));
Altere as propriedades que você deseja personalizar. Existem muitas propriedades, então, segue abaixo apenas um exemplo.
construtorCultura.IsMetric = false;
Para registrar utilize:
construtorCultura.Register();
Agora você já pode utilizar esta cultura em todas as suas aplicações, basta apenas instancia-lá chamando o CultureInfo(“pt-K1”).
E para retirar o registro utilize:
construtorCultura.Unregister(“pt-K1”);
Ordenação de acordo com a cultura
Um conceito muito interessante sobre culturas no .Net é que a mesma influencia na ordenação de caracteres. O caractere “Æ” vem antes “A” na nossa ordenação comum, mas em alemão é diferente, ela vem após a letra “A”. Teste o seguinte exemplo com a cultura “pt-BR” e com a “da-DK”. e verifique que a ordenação automática do .Net mudou a seqüencia do array.
string[] caracteres= new string[] { “A”, “Æ” }; Array.Sort(caracteres);
Pesquisa de string de acordo com a cultura
A pesquisa de index de uma string (“texto”.IndexOf) pode trabalhar de forma diferente para cada cultura. Por exemplo:
Com a cultura “pt-BR”, se você pesquisar o caractere “Æ” dentro de uma string, ele retornará tanto os caracteres “Æ” quanto “AE”, e a mesma coisa quando você estiver procurando o caractere “AE” ele irá retornar tanto o “Æ” quando “AE”. Já com a cultura “da-DK” é diferente, quando você pesquisar por “Æ” só retornará “Æ” e quando você pesquisar por “AE” só retornará “AE”. Faça um teste com o seguinte código abaixo configurando cada teste em uma cultura diferente.
“Æ”.IndexOf(“Æ”); “Æ”.IndexOf(“AE”);
“AE”.IndexOf(“Æ”); “AE”.IndexOf(“AE”);
Para contornar esta situação podemos declarar que as comparações serão independentes da cultura utilizando os seguintes métodos.
“Æ”.IndexOf(“Æ”, StringComparison.InvariantCulture); ou Thread.CurrentThread.CurrentCulture = CultureInfo.InvariantCulture;
Comparação de acordo com a cultura
A comparação de strings também se comporta diferente em cada cultura. Faça o seguinte teste na cultura “pt-BR” e “tr-TR”
string.Compare(“teste”, “TESTE”, true);
Você pode verificar que mesmo mesmo indicando com o “true” que a comparação a ser feita é case-insensitive em “tr-TR”(Turquia-Turco) o retorno é falso indicando que as strings são diferentes. Já em “pt-BR” o retorno me diz que a comparação é verdadeira.
Aprendi muito escrevendo este artigo, espero que gostem.
Fontes:
http://msdn.microsoft.com/pt-br/library/system.globalization.cultureinfo(VS.90).aspx
http://msdn.microsoft.com/pt-br/library/1ztca10y(VS.90).aspx
http://msdn.microsoft.com/pt-br/library/system.threading.thread.currentuiculture(VS.90).aspx
http://www.microsoft.com/learning/en/us/Book.aspx?ID=12915&locale=en-us