Tuesday, August 28, 2012

When should you start testing your game design?

NOTICE: I HAVE MOVED MY BLOG TO MY PERSONAL WEBSITE!
Please check http://www.pmhsilva.com for more details. You can red this blog post here.

I've been reading a lot about Game Design as part of my effort to get back to the gaming industry and one of the subjects that I've been thinking about the most is the amount of testing you should do and, more important, when to start testing the Game Design.

I am a very big fan of iterative development so I believe that making the full game design document, then implement it and then test the game is just plain wrong. That is, unless you're happy with just fixing sowftware bugs and launch the game without considering the testing feedback.

I prefer to start off with an idea and put it down on paper. Just a simple synopsis and eventually some concept art. Then get together with the team of artists and developers (if YOU are the whole team, get together with all aspects of yourself as a game developer) and discuss the concept with them. The output of this should be at least one "level" or prototype of your game. Just like they do on TV series with the pilot. Put one "pilot" together and start testing immediatly!

Why? Well, the more feedback you get, the better! You may think this is an awesome idea and determine that your target will be a specific demographics and when you actually get people from those demographics to actually play your game, they may tell you it is indeed awesome or they may tell you it sucks big time! And you should listen to them! After all, you're doing this game for them, aren't you?

Recently many people resorted to places like Kickstarter to help fund their games. But to attract investment on your games, you have to convince people that your game is good AND give something back. Makes sense. One thing that occurred to me when I was reading this was that maybe one of the advantages Kickstarter investors might like to do is actually be involved in the development process, as testers. Why not? Who better to help you test your game and tune it than those who actually are willing to put money behind it?! But how would these tests work when your testers might be spread around the world? That is in itself a challenge, but totally doable.

Nevertheless, in response to the question that entitles this blog post, I would say that testing should start as soon as you have a prototype, a pilot. Iterate through that and you're game will be much more successful!

Wednesday, August 15, 2012

DJ R.O.B. got mentioned in "Best Entries for Game Career Guide's Game Design Challenge"!

NOTICE: I HAVE MOVED MY BLOG TO MY PERSONAL WEBSITE!
Please check http://www.pmhsilva.com for more details. You can red this blog post here.

I just checked the Results from Game Design Challenge: The Return Of R.O.B. on GameCareerGuide.com's website and I was delighted to see that my submitted idea is among the best entries for this challenge!

If you want to know more about the idea, check the original blog post in where I describe it in detail.

To be perfectly honest, I was a bit sceptic that my idea would be in the best entries as I didn't have enough time to polish the game design concept that much, but I was somehow confident that they would like the idea.

But in the end, the idea went through and I am extremely happy as this feels very rewarding. I will definitely keep working on new Game Designs and I'll keep submitting ideas to GCG's Challenges. It allows me to practice my Game Designing skills and although it is not a game to show, it shows that I have good ideas, the ability to design and make them become a real game. And you'll never know, maybe I'll start making one of this games, one of this days! :D

Finally, I would like to thank Nuno Barreiros again for providing me with the amazing art that was submitted with the idea. I'm quite sure it helped my idea get through!

Cheers!

EDIT: I just noticed that when listing the items on Features list in GameCareerGuide.com's site (http://www.gamecareerguide.com/features/), the icon for the "Results from Game Design Challenge: The Return Of R.O.B." thread is a thumbnail of my submission! :D

Tuesday, August 14, 2012

A huge leap for Mankind indeed

NOTICE: I HAVE MOVED MY BLOG TO MY PERSONAL WEBSITE!
Please check http://www.pmhsilva.com for more details. You can red this blog post here.

I just read a very interesting article about the first week of NASA's Mars mission and I can't help feeling totally overwhelmed and in awe with the findings. Sure, its just a bunch of pictures that show pretty much nothing more than a deserted landscape, with no signs of life as we know it.

But I can't help thinking we are talking about a planet that is really far away from us. Depending on where Earth and Mars are in their respective orbits, they can be anywhere from 58 million to over 400 million kilometers apart. That means if you drove your car there, on an average speed of 120km/h, it would take you between 55 to 380 years (!) to get to Mars... :P

Either way, this is indeed a huge step forward for Mankind and the proof that we are indeed very inquisitive and clever beings, if only we'd focus on doing inquisitive and clever things all the time...

Check the 360 degrees images of Mars' surface below or here.


Curiosity rover: Martian solar day 2 in New Mexico

Tuesday, August 07, 2012

The strange case of Benjamin SWTOR...

NOTICE: I HAVE MOVED MY BLOG TO MY PERSONAL WEBSITE!
Please check http://www.pmhsilva.com for more details. You can red this blog post here.

I just finished reading this article on Gamasutra where the author explains why, in his opinio, SWTOR (Star Wars - The Old Republic) is, let's face it, a failure. Being a huge fan of MMO's and an active World of Warcraft player since late 2006, I also tried SWTOR as most my friends playing it told me this would be the game that would finally challenge WoW.

I decided to try it out, but I quickly understood that, while the leveling experience was indeed very nice, the game was too much focused on single player progression and everyone I talked to said the end-game experience was not that good. Well, this is where World of Warcraft really shines. I started playing WoW because a very close friend incited me to it and I remember his words, back in 2007: "It's when you reach level 70 that the game REALLY begins!". It's true. It's more than true, it is astonishingly true!

While I disagree with the author of the mentioned article that social experience in SWTOR is not that good (WoW's one is even worse imo), the fact that the end game experience is not good actually killed the game. I mean, why would anyone in his perfect sense play SWTOR (and actually PAY to play it) when it is so frustratingly hard and repetitive to actually consume all the (very good) content of the game? I mean, yes, some people could do it once or twice, but then it just becomes too boring...

Anyway, I believe there is a lesson or two to be learned here and most of all, I don't think people should be afraid to base their ideas in other already existing and well established ones. If it is working, why not do the same and improve it. Reinventing the wheel never seems to payout...

I'm sad. I like Star Wars. A lot. They really had the potential to compete with WoW. But they failed.

Better luck next time, EA.

Thursday, August 02, 2012

Game Design Challenge: The Return Of R.O.B.


NOTICE: I HAVE MOVED MY BLOG TO MY PERSONAL WEBSITE!
Please check http://www.pmhsilva.com for more details. You can red this blog post here.


The latest GameCareerGuide.com's Game Design Challenge is about R.O.B., aka Robotic Operating Buddy, an accessory for the Nintendo Entertainment System released in 1985. The challenge is to design a game that uses Nintendo's R.O.B. peripheral somehow. I decided to give it a go and create a Game Design that would not only use R.O.B. as a peripheral, but also gave him the leading role in the game itself. I came up with the idea described below and it would be awesome if you could provide me with any feedback you might have. It would mean the world to me as it would help me to become a better Game Designer! Thanks!

Also, I would like to give due credit to Nuno Barreiros (X-Tense) for the fantastic in-game screenshot he made just for my idea. It was exactly what I had in mind, fantastic work! On to the idea...
---------------------------------------------------------------------------------------------
Game Title: DJ R.O.B.

Background Story: After several years playing secondary roles at different games, R.O.B. decided to take a vacation and travel around the world. Paris, Rome, London, New York, you name it - R.O.B. was there. But when he came back, there was one place that didn't get out of his mind: Ibiza! Oh boy, did he party in Ibiza, met a lot of interesting people, danced all night long... That was the life! R.O.B. felt that the time has come to make a change in his life. And that was it, he was going to be a star: R.O.B. was going to be a DJ!

Basic Concept: In DJ R.O.B. the player plays R.O.B. himself, trying to become the best DJ in the world. To accomplish that, the player must help R.O.B. put together different music pieces to create one masterpiece mix!


Gameplay: The player must help R.O.B. put together pieces of music that are literally coming down on his DJ plates.

These pieces are represented by colored blocks that, when matched in groups of 3 or more of the same color, will be added to the mix increasing the mix's quality and granting R.O.B. music score (points). The higher the score, the better the mix is.

R.O.B. has 4 plates and every one of them has a bonus color, meaning that everytime the player matches 3 or more pieces of a specific color in the plate with the same color bonus active, he will get combo points, increasing the music score.

To be able to do this, the player is able to move the colored blocks right and left while they are moving down to the plates, but as soon as they hit the plate or another block under them, they will stay in that position and will be unable to be moved.

R.O.B. would have two new accessories that should be put on the left and right side of him. Each accessory would have two colored plates, just like the image attached. The player would have to use his controller to tell R.O.B. to turn left of right and touch one colored plate, activating bonus score. The plate R.O.B. touched would grant extra music score. While the player could play alone using his controller, if he used R.O.B. he would be able to have a better score.

Game Objective: For each level, R.O.B. will have a limited amount of time to produce one mix and he has to achieve a certain amount of music score (points) to progress to the next level, otherwise, he will fail his goal of becoming the best DJ in the world.

Score would accumulate on each level and in the end, the player could submit his high score to be able to compare with other DJs around the world.
---------------------------------------------------------------------------------------------

Tuesday, July 31, 2012

Surprise your players and they will love your games


NOTICE: I HAVE MOVED MY BLOG TO MY PERSONAL WEBSITE!
Please check http://www.pmhsilva.com for more details. You can red this blog post here.


I just read this article about how introducing surprise events improves the gamer's learning process. This is the result of a study led by Erik Van der Spek and his colleagues at the University of Utrecht in the Netherlands who used a mod of the game Half Life 2: Episode Two to create a training scenario in which students undertake the role of a medical first responder. The study was published in the British Journal of Educational Technology.

The whole idea was to determine what would be the reaction of players to the introduction of surprising events and how would that affect their learning experience in dealing with the challenges presented to them. Seems like those elements of surprise do improve the learning performance. According to the above mentioned article: 

"It appears that when we are engaged in a scenario we construct a situation model through which we seek to understand and act. For example, the relationships between individuals, what objectives and roles individuals have and how this may change. This situation model is updated as we work through the scenario.

However, as humans we are strongly wedded to our points of view. We will look for information that confirms our view of the world. When we receive information that does not fit with our situation model, we may often choose to ignore it, or even use it to polarize our view. This has been shown in experiments featuring individuals holding extreme political views on the left or right of the spectrum.

When we encounter a surprise in a scenario it grabs our attention and forces us to question the situation model that we have formed. In performing this action, the leaner must go beyond surface learning activities. Instead they must engage in deeper learning processes to compare, contrast and synthesize information presented with this event (Graesser et al, 2009).".

Most forms of art that I have strongly embedded in my mind and that I consider to be the best pieces of art I've been exposed to, are those who surprised me somehow. I remember the first time I saw movies like The Exorcist, Schindler's List, The Ring, The Sixth Sense or Inception. It was the fact that they didn't have a regular story line and had impressive and surprising events that led me to conclude that they are among the best movies I've ever seen. Same thing applies to when I read "The Unbearable Lightness of Being" by Milan Kundera. Might not be the best book ever for you but it totally knocked me off my feet as I have never had someone telling me a story that way before. It was surprising! Same goes for music, painting, etc. and, of course, games.

Hence, I would have to agree that this is an important tool for Game Designers. Being able to create engaging and compelling stories around a game will obviously make the player want to play more. But adding surprising events will not only improve their learning process and performance while playing the game but will also improve their satisfaction and increase their willingness to play more and more of it. Now, isn't that what we all want?

I believe that it is not always possible to include those kind of surprising events, but whenever it is, Game Designers should go for it.

Wednesday, July 25, 2012

Write the game you want to tatoo!


NOTICE: I HAVE MOVED MY BLOG TO MY PERSONAL WEBSITE!
Please check http://www.pmhsilva.com for more details. You can red this blog post here.


I just read Gamasutra's newsletter and I couldn't help noticing one article where Game designer David Jones (Grand Theft Auto, Crackdown, Lemmings) points out something that is quite true when you think about it.

He says that "If I forget about an idea after 4 or 5 months I know it was a good thing we never started it, but if after two years it's still there and you're still excited by it, then that's the time to start thinking about putting it into production."

When you think of it, this is the exact same principle as making a tatoo. I mean, you should only tatoo something that you envision in your head for several years. Then you know that you really would like to have that tatoo'ed in your body permanently. Exactly because it is supposed to stay there forever.

I was already thinking about writing down some of the ideas I have in my head, but now I'm sure that I should start by writing those who have been in my head the longest. On to creative writing we go!

World of Warcraft: Mists of Pandaria to release on September 25th 2012!

NOTICE: I HAVE MOVED MY BLOG TO MY PERSONAL WEBSITE!
Please check http://www.pmhsilva.com for more details. You can red this blog post here.



World of Warcraft: Mists of Pandaria now has an official release date (check this link). It will be released on September 25th 2012. This is my favorite game ever and I already upgraded my version, how about you? (check picture)

Game Design Challenge - Bring it on!


NOTICE: I HAVE MOVED MY BLOG TO MY PERSONAL WEBSITE!
Please check http://www.pmhsilva.com for more details. You can red this blog post here.


I was browsing Gamasutra the other day and I came across an initiative they have called "Game Design Challenge". According to them " GameCareerGuide.com's Game Design Challenge is an exercise in becoming a game developer, asking you to look at games in a new way -- from the perspective of a game creator, producer, marketer, businessperson, and so forth.

Every other Wednesday we'll present you with a challenge about developing video games. You'll have two weeks to brainstorm a brilliant solution (see below for how to submit your answers). After the two week submission period elapses, the best answers and the names of those who submitted them will be posted.".

I thought, why not? I have many Game Design ideas, why not challenge myself into actually putting pen to paper? It's not about the competition, its more about actually starting to write stuff down. The initiative is awesome, if you ask me. Last challenge was to design a game for the recently announced Microsoft Surface (check this) and I was curious to see the outcome. The top 3 ideas were just amazing. I liked the one on 2nd place more than the 1st placed design, but hey, I'm not in the panel of judges! :)

Looking forward to see what is the next challenge and I will enter the competition. Bring it on! :)

Thursday, July 19, 2012

My passion for games... lives on!

NOTICE: I HAVE MOVED MY BLOG TO MY PERSONAL WEBSITE!
Please check http://www.pmhsilva.com for more details. You can red this blog post here.

In early 2007, me and a long time friend also in the IT area found out that we shared an immense passion about Video Games and decided to put to use our already considerable experience in the field of IT to try and create a Games Studio in Lisbon, Portugal.

We had several brilliant ideas and we were almost certain we would change the world of video games with them and make great, fun things. I still do.

My role was to do Product and Project Management, Game Design of the ideas I had in my head to create games and since I was fluent in English and had strong communication skills, I was also appointed to do Business Development, i.e. make sure our games got published.


During the time our Game Studio was able to survive the lack of investment from anyone else but ourselves, I was able to work on some brilliant titles, with fantastic ideas and extremely skilled individuals.

The first game we produced was called Steam, a Match 3/Action/Puzzle full 3D casual game, where you would have to help a cute young girl travel across the land of Sweetopia, fighting “the bad guys” with her train who fired colourful marble balls to unveil the secret of the stolen candies! We successfully managed to get the game published with Oberon Media, Big Fish Games, Boonty and a couple other publishers. The game can still be played today here as it still is being sold.

The second title I worked on was Starwheels, a Car Racing Full 3D casual game. The idea was very good and the concept was awesome as well as the graphics. One of my key participation in this game had to do with making sure the whole art was extremely polished and had to look cute, as the game was intended to be played by women mostly. The players would have to instantly relate to the characters and cars, and the cars had no major differences apart from the aesthetic ones. As with Steam, I was also producing the game and was responsible for making sure the team (the same as in Steam) met deadlines and implemented the game features as they were in the design document. Although the game never got to be published, every single Publisher I pitched the game to was impressed by the game’s graphics and its fluidity.

At the same time as with Starwheels, we had a different team working on a game called Netliga (a Web game). It was a Soccer Management Game and the user had to pick a team of players based on the official players who played in the Portuguese Premier League. The user would then define his team’s starting line-up and the game engine would rate the players depending on their performance in real life. It would then compute the results and determine each user team’s score. In this game, I designed the game concept, gameplay and other game elements, the general layout guidelines and also did some development on the starting line-up UI. I also managed to have it published by Sapo.pt, the largest portuguese web portal, on its game section and at its highest peak we had over 10000 active players registered and playing.

Later on, we created another game called Ignite Factory, an Online Action Game. The storyline was intended to show the day-to-day work of our studio and the characters of the game represented each of the studio members. Each one of us was supposed to do something, from creating the raw materials to transforming them with the goal of producing lighters (our games!). My character was responsible to make sure the bits and pieces got together moving from one production phase to the other (kind of mimicking what I did in real life). The game is still playable here and its one of the things I liked doing the most, despite being a very simple flash game.

The last big project I worked on was Portucale, a Real Time Strategy Web Game similar to the very popular Travian or Tribal Wars. The concept was to have a player impersonate one Knight, servant to the first King of Portugal and help the Kingdom grow, always respecting the real events of the History of Portugal. The game started in 1143 (when Portugal was created) and ended in 1910 (when the Monarchy was abolished and the Republic implemented in the country) and the player would evolve with time, accordingly to Portugal and the world’s history and technological evolutions as well. The storyline was very rich and full of historical events and I tried to always keep a pedagogical approach to every aspect of the game as I wanted to be a way for young people to learn a little bit more about their country’s history while having fun! I was also very careful to make sure the environment of the game evolved as the years passed by and major historical events took place (the Arab Wars, the Sea Conquest, the Napoleon Invasions, The Industrial Revolution, etc.).

Sadly, this game came in a late phase of our game studio’s life and we didn’t get any funding for it so it eventually followed the studio’s demise and had to be stopped.

Before our studios had to close, I had the opportunity to work on the design of a game that never got past the initial concept and prototype phase code-named “Pet Doctor”. It was a game aimed for the casual games biggest audience (women in their mid-30s/40s) and the idea was to have a pet with some kind of condition (never anything too serious) and the player would control a cute female veterinarian that would have to use mouse gestures to heal the pet. My idea was to have a cool subject (save/help animals) with innovative technologies (gesture recognition) as I was already looking at having a mobile/tablet touchable version of this game as well. Unfortunately, the studio had to close and the game never got to be done.

Nevertheless, my unending passion for games continues and I still dream of having a career in the gaming industry. Maybe one of these days... ;)

Sunday, July 01, 2012

A new beginning...

I know it has been a while since I came here but there has been many changes recently in  my life. Important changes. Earlier this year, I decided to move out of Lisbon, Portugal, my hometown and the place where I lived whole my life. As you might easily understand, it was not an easy decision. Leaving everything you know behind, family and friends, even the places you used to know and be every single day. Trust me when I say it is not easy.

But things in Portugal are becoming increasingly unbearable and many people took the same route I did. Actually, according to official numbers, 180.000 people left the country in 2010, 200.000 in 2011 and apparently, in 2012 we'll reach 250.000 people leaving Portugal in search for a better life elsewhere. A friend of mine told me the other day "This is the most well educated and technically prepared generation in the last 50 years and everyone is leaving the country." So, the question is, why is everyone leaving? That is a good yet very complicated question with political and economic implications and this blog is not about politics or economics, so I'll leave it up to you to think about it.

Moving on, I decided to leave the country and the first question was: where to? I considered several options: Germany, Netherlands, France, Sweden, Denmark, UK and Ireland. You might think, all are countries with good economy and quality of living except maybe Ireland, as the country was also in a delicate situation, just like Portugal, Greece and now Spain. Well, it turned out that from all the mentioned countries, it was the one I chose and I honestly believe it is the best choice. I am an IT professional and Ireland is the place where every single multinational IT company has its European HQ. In Ireland you have Microsoft, Google, Apple, Facebook, IBM, Oracle and many many other IT "giants". Besides that, some of the biggest Gaming companies (games are my unending passion) also are established in Ireland somehow (Activision, Electronic Arts, Riot Games, etc). On top of that, salaries are in par with all the other countries and the cost of living (rent, etc) is a bit cheaper, as well as taxes. Seemed a natural choice.

So, here I am! I've been living and working in Dublin since April 2012 and all I can say is that I totally love the city and its people. Yeah, sure, the weather sucks big time, but hey, the sun doesn't pay bills at the end of the month, right?

Also, since I've been here, I have way more time to do so many things I love doing and didn't had the time to do when I was in Portugal. Worse part is: I miss my son and daughter. A lot. There's no day that goes by that I don't miss them dearly. But hey, that's life.

And my life has just gave me a new beginning.

Thursday, February 24, 2011



Here's my very first Android app, a "Hello World"-like app!
Quite easy tbh!


Back on track!

Yes, I am back. Hope that this time is for good.

Right now I'm getting into WPF, Silverlight and all the wonderful things that .NET 4.0 brought us.

Also doing a very interesting hands-on approach on Android Development, so keep posted for updates.

Also follow me on twitter and LinkedIn!

Monday, July 09, 2007

MAIS UMA INTRO À .NET FRAMEWORK 2.0 - Parte V

Capítulo 2 – Operações de Input e Output (I/O)

Este capítulo fala de um tipo de operações mais comum em programação, ou seja, a manipulação de ficheiros e/ou do sistema de ficheiros. As características básicas do sistema de I/O da .NET Framework 2.0 incluem aceder a ficheiros e pastas no sistema de ficheiros, trabalhar com streams de leitura e escrita, a utilização de streams de compressão e utilizar dispositivos de armazenamento.
1. Navegar no Sistema de Ficheiros

1.1. Quais são as classes do Sistema de Ficheiros?

Dentro do namespace System.IO existe um conjunto de classes que são utilizados para navegar e manipular ficheiros, pastas e drives. As classes do sistema de ficheiros estão divididas em dois tipos de classes: informativas e utilitárias.
A maioria das classes informativas derivam da classe base FileSystemInfo e expõem toda a informação sobre objectos do sistema de ficheiros. Como exemplos, temos as classes FileInfo e DirectoryInfo.
Adicionalmente, a classe DriveInfo representa uma drive no sistema de ficheiros, mas embora seja uma classe informativa, não deriva da classe FileSystemInfo devido ao facto de não partilhar um conjunto comum de comportamentos (por exemplo, é possível apagar ficheiros e pastas, mas não é possível apagar drives).

As classes utilitárias disponibilizam um conjunto de métodos estáticos para realizar determinadas operações em objectos do sistema de ficheiros tais como ficheiros, pastas e caminhos do sistema de ficheiros. Estas classes utilitárias incluem as classes File, Directory e Path, por exemplo.

1.2. A classe FileSystemInfo

A classe FileSystemInfo disponibiliza funcionalidades básicas para todas as classes informativas do sistema de ficheiros. As suas propriedades mais importantes são:
· Attributes: Obtém ou define FileAttributes de um ficheiro ou directório;
· CreationTime: Obtém ou define a hora em que um dado ficheiro ou directório foi criado;
· Exists: Verifica se um ficheiro ou directório existe no sistema de ficheiros;
· Extension: Obtém uma string com a extensão de um ficheiro ou directório;
· FullName: Obtém o caminho completo no sistema de ficheiros para um dado ficheiro ou directório;
· LastAccessTime: Obtém ou define a hora do último acesso a um ficheiro ou directório;
· LastWriteTime: Obtém ou define a hora da última escrita num ficheiro ou directório;
· Name: Obtém o nome simples de um ficheiro ou directório. Para um ficheiro, retorna o nome de um ficheiro num directório, para um directório retorna o último nome de directório na hierarquia do directório.

Para além destas propriedades, os métodos mais importantes desta classe são:
· Delete: Elimina um ficheiro ou directório do sistema de ficheiros;
· Refresh: Actualiza a informação da classe, com a informação mais actualizada sobre o sistema de ficheiros.

1.3. A classe FileInfo

A classe FileInfo disponibiliza funcionalidades que permitem aceder e manipular um ficheiro do sistema de ficheiros. As suas propriedades mais importantes são:

· Directory: Obtém o objecto DirectoryInfo que representa o directório em que o ficheiro está armazenado;
· DirectoryName: Obtém o nome do directório em que o ficheiro está armazenado;
· IsReadOnly: Obtém ou define a possibilidade do ficheiro ser alterado e/ou apagado;
· Length: Obtém o comprimento do ficheiro.

Os métodos mais importantes desta classe são:

· AppendText: Cria um novo objecto do tipo StreamWriter que permite a adição de texto ao ficheiro;
· CopyTo: Copia o ficheiro para uma nova localização;
· Create: Cria um novo ficheiro baseado na informação corrente sobre o ficheiro;
· CreateText: Cria um novo objecto do tipo StreamWriter e um novo ficheiro para escrita de texto;
· Decrypt: Decifra um ficheiro que foi cifrado pelo utilizador actual;
· Encrypt: Cifra um ficheiro de forma a que apenas o utilizador actual possa decifrar a informação nele constante;
· MoveTo: Move um ficheiro para uma nova localização;
· Open: Abre um ficheiro com privilégios específicos;
· OpenRead: Abre um ficheiro apenas com permissões de leitura;
· OpenText: Abre um ficheiro e devolve um objecto do tipo StreamReader para permitir a leitura do texto do ficheiro;
· OpenWrite: Abre um ficheiro apenas com permissões de escrita;
· Replace: Substitui um ficheiro com a informação no objecto FileInfo corrente.
Para obter informação sobre um determinado ficheiro é necessário criar um novo objecto do tipo FileInfo utilizando o caminho para o ficheiro. De seguida, aceder às propriedades do ficheiro, como se pode ver no exemplo abaixo:
FileInfo oMeuFicheiro = new FileInfo(@”c:\boot.ini”);
if (oMeuFicheiro.Exists())
{
Console.WriteLine(“Nome do Ficheiro : {0}”, oMeuFicheiro.Name);
Console.WriteLine(“Caminho : {0}”, oMeuFicheiro.FullName);
}
Utilizando o objecto FileInfo desta forma permite-nos obter informação sobre um ficheiro no sistema de ficheiros.
Da mesma forma, a classe FileInfo permite operações sobre ficheiros sem ser apenas consulta das suas propriedades. Como exemplo deste tipo de operações, temos o código seguinte que copia um ficheiro para outro local no sistema de ficheiros:
FileInfo oMeuFicheiro = new FileInfo(@”c:\boot.ini”);
oMeuFicheiro.CopyTo(“c:\boot.bak”);
O mesmo tipo de procedimento pode ser utilizado para criar ou mover ficheiros.
1.4. A classe DirectoryInfo

A classe DirectoryInfo disponibiliza funcionalidades que permitem aceder e manipular um directório do sistema de ficheiros. As suas propriedades mais importantes são:

· Parent: Obtém o objecto DirectoryInfo que representa o directório-pai do directório actual;
· Root: Obtém a parte da raíz do caminho do directório numa string.

Os métodos mais importantes desta classe são:

· Create: Cria um novo directório baseado na informação corrente sobre o directório;
· CreateSubdirectory: Cria um novo directório como directório-filho do directório corrente;
· GetDirectories: Obtém um array de objectos DirectoryInfo que representam os subdirectórios do directório corrente;
· GetFiles: Obtém um array de objectos FileInfo que representam todos os ficheiros constantes no directório corrente;
· GetFileSystemInfos: Obtém um array de objectos FileSystemInfo que representam todos os ficheiros e subdirectórios constantes no directório corrente;
· MoveTo: Move um directório para uma nova localização.
Aceder aos ficheiros de um directório é muito semelhante a aceder à informação sobre um ficheiro. Basta criar um objecto do tipo DirectoryInfo válido utilizando o caminho para o directório e, posteriormente, invocar o método GetFiles para enumerar os ficheiros de um directório:
DirectoryInfo oMeuDirectorio = new DirectoryInfo(@”c:\windows”);
Console.WriteLine(“Directório : {0}”, oMeuDirectorio.FullName);
foreach (FileInfo ficheiro in oMeuDirectorio.GetFiles())
{
Console.WriteLine(“Ficheiro : {0}”, ficheiro.Name);
}
1.5. A classe DriveInfo

A classe DriveInfo disponibiliza funcionalidades básicas que permitem aceder e manipular uma drive do sistema de ficheiros. As suas propriedades mais importantes são:

· AvailableFreeSpace: Obtém o tamanho de espaço disponível na drive. O espaço retornado pode ser diferente do valor retornado pela propriedade TotalFreeSpace, dependendo das quotas de disco;
· DriveFormat: Obtém o formato da drive (FAT32 ou NTFS);
· DriveType: Obtém o tipo de drive na forma da enumeração DriveType;
· IsReady: Obtém o estado da drive, indicando se está pronta a ser utilizada;
· Name: Obtém o nome da drive;
· RootDirectory: Obtém um objecto DirectoryInfo que representa o directório raíz da drive;
· TotalFreeSpace: Obtém o espaço total livre da drive;
· TotalSize: Obtém o tamanho total da drive;
· VolumeLabel: Obtém ou define a etiqueta da drive. Apenas pode ser definido para drives que não são apenas de leitura (read only).
O método mais importante desta classe é o método GetDrives. É um método estático que retorna todas as drives do sistema corrente.
1.6. A enumeração DriveType

A enumeração DriveType disponibiliza os tipos possíveis de drives que podem ser representados por um objecto DriveInfo. Esta é composta pelos seguintes membros:

· CDRom: Uma drive óptica. Pode ser um CD-Rom um DVD, etc.;
· Fixed: Um disco rígido;
· Network: Uma drive de rede mapeada;
· NoRootDirectory: Uma drive que não possui directório raíz;
· Ram: Uma drive RAM;
· Removable: Uma drive amovível;
· Unknown: Uma drive para a qual não se consegue determinar o tipo.
Para enumerar as drives de um sistema de ficheiros, é necessário chamar o método GetDrives da classe DriveInfo. Depois, percorre-se o array de objectos DriveInfo retornados pelo método:
DriveInfo[] drives = DriveInfo.GetDrives();
foreach (DriveInfo drive in drives)
{
Console.WriteLine(“Drive: {0}”, drive.Name);
Console.WriteLine(“Tipo: {0}”, drive.DriveType);
}

De notar que todas as drives ópticas (CD, CD/R, DVD, DVD/R, etc.) são marcadas como sendo do tipo DriveInfo.CDRom.
1.7. A Classe Path

A classe Path disponibiliza métodos que permitem aceder e manipular um caminho do sistema de ficheiros. Os métodos mais importantes desta classe são:

· ChangeExtension: Utiliza um caminho existente e retorna um novo caminho com a extensão do nome do ficheiro alterada. De notar que apenas a string do caminho é alterada, a extensão do ficheiro mantém-se;
· Combine: Combina duas strings de caminho compatíveis;
· GetDirectoryName: Retorna o nome do directório do caminho especificado;
· GetExtension: Retorna o nome da extensão de um ficheiro no caminho especificado;
· GetFileName: Retorna o nome de um ficheiro no caminho especificado;
· GetFileNameWithoutExtension: Retorna o nome de um ficheiro sem a extensão, no caminho especificado;
· GetFullPath: Retorna o caminho completo para o caminho especificado. Este método pode ser utilizado para resolver caminhos relativos;
· GetPathRoot: Retorna o nome do directório raíz no caminho especificado;
· GetRandomFileName: Gera um nome de ficheiro aleatoriamente;
· GetTempFileName: Gera um ficheiro temporário no sistema e retorna o caminho completo para esse ficheiro;
· GetTempPath: Retorna o caminho do directório de ficheiros temporários para o utilizador ou sistema correntes;
· HasExtension: Indica se o nome do ficheiro de um determinado caminho possui extensão;
· IsPathRooted: Indica se o caminho especificado inclui um directório raíz.
A classe Path permite-nos interrogar e trabalhar as partes individuais de um caminho do sistema de ficheiros. Em vez de escrevermos o nosso próprio código de tratamento dessas partes individuais de um caminho, a classe Path permite-nos responder às questões mais comuns sobre um caminho do sistema de ficheiros. Se quisermos, por exemplo, alterar a extensão de um ficheiro, poderíamos efectuá-lo, da seguinte forma:

string oMeuCaminho = @“C:\boot.ini”;
Console.WriteLine(oMeuCaminho);
Console.WriteLine(“Extensão: {0}, Path.GetExtension(oMeuCaminho));
Console.WriteLine(“Alterado: {0}, Path.ChangeExtension(oMeuCaminho, “bak”));
1.8. A Classe FileSystemWatcher

Uma das tarefas mais comuns em programação é monitorizar o sistema de ficheiros para determinar quando acontecem alterações e reagir de acordo com elas. A classe FileSystemWatcher disponibiliza funcionalidades básicas que permitem monitorizar um sistema de ficheiros e verificar alterações. As suas propriedades mais importantes são:

· EnableRaisingEvents: Obtém ou define se o objecto de monitorização deve despoletar eventos. Normalmente, esta propriedade é utilizada para ligar ou desligar a monitorização de ficheiros e/ou directórios;
· Filter: Obtém ou define o filtro de ficheiros que é utilizado para determinar quais as alterações de ficheiros a monitorizar. Um filtro vazio indica todos os ficheiros;
· IncludeSubDirectories: Obtém ou define se a monitorização de um directório deve ou não estender-se aos seus subdirectórios;
· NotifyFilter: Obtém ou define o tipo de alterações a monitorizar. Por defeito, todas as alterações (criação, eliminação, renomeação e modificação) são notificados;
· Path: Obtém ou define o caminho do directório a monitorizar.
O método mais importante desta classe é o método WaitForChanged. É um método síncrono utilizado para monitorizar alterações a um directório e retornar uma estrutura que contém todas as alterações efectuadas.
Para além disso, esta classe possui um conjunto de eventos utilizados no processo de monitorização de um directório, designadamente:

· Changed: Ocorre quando um ficheiro ou directório foram alterados no directório monitorizado;
· Created: Ocorre quando um ficheiro ou directório foram criados no directório monitorizado;
· Deleted: Ocorre quando um ficheiro ou directório foram eliminados no directório monitorizado;
· Renamed: Ocorre quando um ficheiro ou directório foram renomeados no directório monitorizado.
Assim, para monitorizar as alterações a um directório, é necessário criar um objecto FileSystemWatcher, especificando a propriedade Path. Depois, registar os eventos que se pretendem monitorizar e, finalmente, permitir a captura de eventos, colocando a propriedade EnableRaisingEvents a true:
// Criar o objecto FileSystemWatcher
FileSystemWatcher bigbrother = new FileSystemWatcher();
bigbrother.Path = @”C:\”;
// Registar os eventos
bigbrother.Created += new FileSystemEventHandler(bigbrother_changed);
bigbrother.Deleted += new FileSystemEventHandler(bigbrother_changed);
// Começa a monitorização
bigbrother.EnableRaisingEvents = true;
// Event Handler
static void bigbrother_changed(object sender, FileSystemEventArgs e)
{
Console.WriteLine(“O directório foi alterado ({0}): {1}”,
e.ChangeType, e.FullPath);
}

Sunday, July 01, 2007

MAIS UMA INTRO À .NET FRAMEWORK 2.0 - Parte IV

Artigo anterior: MAIS UMA INTRO À .NET FRAMEWORK 2.0 - Parte III

4. Conversões entre tipos

Conversões entre tipos é uma das tarefas mais comuns em programação orientada a objectos. Várias vezes é necessário converter uma variável de um tipo para outro antes de a passar como parâmetro de um método ou para executar determinado cálculo.
Existem dois tipos de conversões: implícitas e explícitas. Em C# só é possível efectuar conversões implícitas entre tipos se não existir perda de precisão. Isto significa que é possível efectuar conversão implícita em C# apenas e só se o tipo de destino puder aceitar todos os valores possíveis para o tipo de origem. Isto é denominado de conversão generalista (widening conversion):

int i = 1;
double d = 1.0001;
d = i; // Conversão implícita permitida

Se o espectro de conversão ou precisão do tipo de origem excede o do tipo de destino, a operação denomina-se de conversão de estreitamento (narrowing conversion) e normalmente necessita que a conversão seja explícita. Existem várias formas de realizar uma conversão explícita:
System.Convert: Converte entre tipos que implementem o Interface System.IConvertible;
(tipo) cast: Converte entre tipos que definem operadores de conversão;
tipo.ToString e tipo.Parse: Converte entre objectos do tipo string e objectos de tipos base. Se a conversão não for possível, a operação resulta numa excepção;
tipo.TryParse e tipo.TryParseExact: Converte entre um objecto do tipo string e um objecto de um tipo base. Se a conversão não for possível, devolve um valor false;
Os métodos TryParse, TryParseExact e TryCast são novos na .NET Framework 2.0. Conversões explícitas falham se o valor de origem exceder o espectro de valores aceites pelo tipo de destino ou se a conversão entre os tipos não estiver definida, por isso este tipo de conversões deve ser incluído entre blocos Try ou utilizando os métodos TryCast ou TryParse para ser possível verificar o valor de retorno.

4.1. O que é Boxing e Unboxing?

Boxing acontece quando se converte um tipo de valor para um tipo por referência e Unboxing é, naturalmente, o processo inverso, ou seja, a conversão de um tipo de referência para um tipo de valor. No exemplo seguinte podemos ver um exemplo de Boxing através da conversão de um Integer (tipo de valor) para um Object (tipo por referência):

int i = 123;
object o = (object)i; // Conversão através da utilização de um type cast
Da mesma forma, se pode exemplificar o processo inverso, ou seja, Unboxing:
object o = 123;
int i = (int)o;
As operações de Boxing e Unboxing implicam um acréscimo de processamento (overhead), por isso devem ser evitados quando se programam tarefas que se repetem de uma forma intensa. Boxing também ocorre quando são chamados métodos virtuais de uma estrutura que herde de System.Object como, por exemplo, o método ToString(). Assim, para evitar procedimentos de Boxing desnecessários deve ter-se as seguintes sugestões em consideração:

• Implementar versões específicas por tipo (overloads) de métodos que aceitem vários tipos de valor. É uma boa prática criar várias versões do mesmo método com parâmetros diferentes e definitivamente é melhor solução do que implementar apenas uma versão desse método que aceite um parâmetro do tipo Object;
• É preferível utilizar genéricos sempre que possível em vez de utilizar argumentos do tipo Object;
• Quando estamos a criar as nossas próprias estruturas, devemos fazer as nossas próprias versões (override) dos métodos virtuais ToString, Equals e GetHash.

4.2. Como implementar conversão em tipos customizados?

Existem várias formas de definir conversões entre tipos definidos por nós. A técnica a escolher depende do tipo de conversão que pretendemos executar:

• Deve definir-se operadores de conversão para simplificar conversões implícitas e explícitas entre tipos numéricos. De notar que os operadores de conversão são uma novidade na .NET Framework 2.0;
• Deve implementar-se versões customizadas (override) do método ToString para conversões onde o objecto de destino é do tipo string e do método Parse para conversões em que o objecto de origem é do tipo string;
• Deve implementar-se o interface IConvertible para permitir conversões utilizando System.Convert, especialmente quando se implementam conversões específicas de uma cultura (Culture);
• Deve implementar-se uma classe TypeConverter para permitir conversão em Design Time, que será utilizada na janela de propriedades do Visual Studio (para mais informações, visitem http://msdn2.microsoft.com/en-us/library/37899azc.aspx).
A definição de operadores de conversão permitem a atribuição directa de um tipo de valor para um tipo customizado. Deve usar-se a instrução implicit para conversões que não impliquem perda de precisão e a instrução explicit para conversões que estão sujeitas a perda de precisão:
struct TipoA
{
public int Value;

// Permite conversão implícita de um inteiro
public static implicit operator TipoA(int arg)
{
TipoA res = new TipoA();
res.Value = arg;
return res;
}

// Permite conversão explícita para um inteiro
public static explicit operator int(TipoA arg)
{
return arg.Value;
}

// Permite conversão para String (evita Boxing)
public override string ToString()
{
return this.Value.ToString();
}
}

O exemplo anterior também efectua uma costumização do método virtual ToString par evitar o processo de Boxing. Agora seria possível atribuir inteiros a este tipo costumizado, como se pode ver abaixo:
TipoA a;
int i;
// Conversão implícita é aceite
a = 42; // Em vez de se utilizar a.Value = 42
// Conversão explícita
i = (int)a; // Em vez de i = a.Value
Console.WriteLine(“a = {0}, i = {1}”, a.ToString(), i.ToString());

Para se implementar o interface System.IConvertible é necessário especificar isso na definição do tipo costumizado. A implementação deste interface implica definir implementação para 17 métodos, incluindo GetTypeCode, ChangeType e ToType para cada tipo base com o qual se pretenda efectuar operações de conversão. Não é necessário implementar todos os métodos e alguns, como por exemplo ToDateTime, serão provavelmente inválidos. Para métodos inválidos, basta criar uma excepção. Após a implementação do interface IConvertible, pode-se converter a nossa classe utilizando System.Convert:

TipoA a;
bool b;
a = 1;
// Conversão utilizando ToBoolean
b = Convert.ToBoolean(a);
Console.WriteLine(“a = {0}, b = {1}”, a.ToString(), b.ToString());

Tuesday, May 15, 2007

MAIS UMA INTRO À .NET FRAMEWORK 2.0 - Parte III

Artigo anterior: MAIS UMA INTRO À .NET FRAMEWORK 2.0 - Parte II


3. Classes

A .NET Framework possui milhares de classes e cada uma delas possui diferentes métodos e propriedades. Contudo, a .NET Framework está implementada de uma forma bastante consistente, que permite que diferentes classes implementem os mesmos métodos e/ou propriedades da mesma forma.
Esta consistência é possível devido à existência dos conceitos de herança e interfaces, de que vou falar de seguida.

3.1. Herança

A herança de classes deve ser utilizada quando se pretende criar novas classes a partir de classes existentes, ou seja, quando se pretende criar uma classe nova que reaproveite funcionalidades de uma classe existente.
Por exemplo, a classe Bitmap herda da classe Image, mas a classe Bitmap implementa funcionalidades que não existem na classe Image. Isto significa que é possível utilizar uma instância da classe Bitmap da mesma maneira que se utilizaria uma instância da classe Image. Contudo, a classe Bitmap disponibiliza métodos adicionais que permitem ao programador realizar outro tipo de tarefas com imagens.
Outro exemplo é a criação de excepções customizadas que herdem da classe System.ApplicationException ou da classe System.Exception:
class DerivedException : System.Exception {
public override string Message {
get { return “Ocorreu uma excepção na Aplicação!”; }
}
}
É possível capturar e lançar excepções utilizando esta classe, visto que ela herda o seu comportamento da sua classe base (System.Exception):
try {
throw new DerivedException();
} catch (DerivedException dex) {
Console.WriteLine(“Origem: {0}, Erro: {1}”, dex.Source, dex.Message);
}
De notar que, para além desta classe suportar o comportamento de lançamento e captura de excepções, também suporta o membro Source (entre outros), uma vez que este foi herdado da classe System.Exception.
Outro benefício da herança é a possibilidade de utilizar classes derivadas (outro termo para classes herdadas) numa perspectiva de intercâmbio. Por exemplo, existem cinco classes derivadas de System.Drawing.Brush: HatchBrush, LinearGradientBrush, PathGradientBrush, SolidBrush e TextureBrush. O método Graphics.DrawRectagle requer um objecto do tipo Brush como um dos seus parâmetros. Mas, neste caso, nunca se passa um objecto do tipo da classe base (Brush), mas sim um objecto do tipo de uma das suas classes derivadas. Uma vez que todos eles herdam da mesma classe base, o método Graphics.DrawRectangle aceitará qualquer um deles. Da mesma forma, se criarmos uma classe derivada de Brush, esta poderia ser utilizada para passar para o método Graphics.DrawRectangle.

3.2. Interfaces

Os Interfaces, também conhecidos como contratos, definem um conjunto de membros que todas as classes que implementem o interface devem disponibilizar. Por exemplo, o interface IComparable define um método CompareTo, que permite que duas instâncias de uma classe sejam comparadas para verificar semelhança. Assim, todas as classes que implementem este interface, sejam elas classes costumizadas ou classes pertencentes à .NET Framework, devem implementar funcionalidade para o método CompareTo, de forma a serem comparáveis por semelhança.
É ainda possível criarmos os nossos próprios interfaces. Os interfaces são úteis quando é necessário criar múltiplas classes que têm um comportamento semelhante e podem ser utilizadas numa perspectiva de intercâmbio. Por exemplo, este bloco de código define um interface com três membros:
interface IMessage {
// Envia a mensagem. Retorna true se bem sucedida, false se falhar.
bool Send();
// A mensagem a enviar.
string Message { get; set; }
// O endereço para onde enviar.
string Address { get; set; }
}

Depois, se uma classe implementasse este interface, teríamos algo do género:
class EmailMessage : IMessage {
// Temos de implementar os membros do interface
public bool Send() {
.... // Implementação
}
public string Message {
get { ... }
set { ... }
}
public string Address {
get { ... }
set { ... }
}
// Outros métodos
.... // Implementação
}
As classes podem implementar vários interfaces. Assim, é possível uma classe implementar os interfaces IComparable e IDisposable, entre outros.

3.3. Classes Parciais

Classes parciais são um conceito novo na .NET Framework 2.0. Basicamente, as classes parciais permitem que a definição de uma classe (e a sua implementação) seja dividida por diferentes ficheiros de código-fonte. A vantagem desta aproximação está no facto de se esconderem detalhes da definição de uma classe, permitindo que as classes derivadas se concentrem nas partes da implementação mais relevantes para si.
Um exemplo de classes parciais são as classes que definem o inteface gráfico (GUI) de um Windows Form. Num ficheiro (normalmente com o nome [Nome do Form].Designer.cs) temos a definição e declaração de todos os aspectos relacionados com o desenho e propriedades dos controlos que o Form contém. Noutro ficheiro (para o mesmo exemplo teríamos [Nome do Form].cs) teremos a outra parte da classe parcial, em que se definiria a implementação propriamente dita das funcionalidades do Form (Event Handlers, métodos internos, variáveis de trabalho, etc.).

3.4. Genéricos

Genéricos também são um conceito novo da .NET Framework 2.0. Basicamente são uma parte do Sistema de Tipos da .NET Framework que permite a definição de um tipo deixando alguns detalhes por especificar. Em vez de se especificar os tipos dos parâmetros ou classes membros, pode-se permitir que o código que usa o nosso tipo que especifique esses detalhes. Isto permite que o código que consome os tipos especifique o tipo dos membros que usa de acordo com as suas necessidades.
A .NET Framework 2.0 inclui várias classes genéricas no namespace System.Collections.Generic, incluindo o Dictionary, Queue e SortedList. Estas classes funcionam da mesma forma que os seus equivalentes não genéricos no namespace System.Collections mas oferecem melhor performance e segurança de tipos.
Entre as vantagens de utilizar genéricos, pode-se destacar:
  • Menor número de erros de execução de código (runtime) – O compilador não consegue detectar erros de conversão de tipos de e para objectos do tipo Object. De qualquer forma, podem especificar-se restrições para as classes que usam genéricos, permitindo assim ao compilador que detecte tipos incompatíveis.
  • Melhor performance – Efectuar conversões de tipos requer encapsulamento (mais informação no ponto seguinte: Conversões entre tipos), que requer tempo de processador e abranda a performance. A utilização de genéricos não precisa de conversão ou encapsulamento, aumentado a performance.

3.4.1. Como criar Tipos Genéricos

Vamos observar a diferença entre duas classes, uma normal (Obj) e outra genérica (Gen):

class Obj {
public Object t;
public Object u;
// Construtor
public Obj(Object _t, Object _u){
t = _t;
u = _u;
}
}

class Gen<T, U> {
public T t;
public U u;
// Construtor
public Gen(T _t, U _u){
t = _t;
u = _u;
}
}

Como se pode observer, a classe Obj tem dois membros do tipo Object. A classe Gen, por sua vez, tem dois membros do tipo T e U. O código que consumir esta classe genérica é que vai determinar os tipos de T e de U. Dependendo da forma como esse código irá usar a classe Gen, T e U podem ser do tipo string, int, uma classe qualquer ou qualquer combinação daí resultante.

Contudo, existe uma limitação importante na criação de uma classe genérica. Esta será válida apenas e só se compilar com todas as possíveis construções do genérico, sejam elas do tipo int, string ou de qualquer outra classe. Basicamente, estamos limitados ao objecto de base Object quando escrevemos código genérico. Estas limitações não se aplicam ao código que consome o genérico, uma vez que este declara os tipos do código genérico.

3.4.2. Como consumir Tipos Genéricos

Quando se consome um tipo genérico, deve-se especificar os tipos de cada genérico utilizado. Pegando no exemplo anterior, poderíamos ter:

// Adicionar duas strings utilizando a classe Obj
Obj oa = new Obj(“Olá,”, “ Mundo!”);
Console.WriteLine((string)oa.t + (string)oa.u);

// Adicionar duas strings utilizando a classe Gen
Gen<string, string> ga = new Gen<string, string>(“Olá,”, “ Mundo!”);
Console.WriteLine(ga.t + ga.u);

// Adicionar um double e um int utilizando a classe Obj
Obj ob = new Obj(10.125, 2005);
Console.WriteLine((double)ob.t + (int)ob.u);

// Adicionar um double e um int utilizando a classe Gen
Gen<double, int> gb = new Gen<double, int>(10.125, 2005);
Console.WriteLine(gb.t + gb.u);

Como se pode facilmente observar do código acima, ambas as classes produzirão exactamente o mesmo resultado, contudo a classe Gen executará mais rapidamente devido ao facto de não necessitar de encapsulamento (boxing e unboxing) a partir da classe Object.

3.4.3. Como utilizar restrições em Tipos Genéricos

Os genéricos seriam extremamente limitados se apenas se pudesse escrever código que compilasse para qualquer classe, uma vez que estaríamos limitados às capacidades da classe base Object. Para superar esta limitação, os genéricos podem utilizar restrições para definir requerimentos nos tipos que o código que consome o genérico usa para esse mesmo genérico. Os genéricos suportam quatro tipos de restrições:

  • Interface – Determina que apenas tipos que implementem interfaces possam consumir o genérico.
  • Classe Base – Apenas tipos que equivalem ou herdam de uma determinada classe base podem consumir o genérico.
  • Construtor – Requer que o código que consome o genérico implemente um construtor sem parâmetros.
  • Tipo de Referência ou Valor – Requer que o código que consome o genérico seja um tipo de referência ou de valor.

Para definir uma restrição a um genérico, deve usar-se a cláusula where na definição do genérico:

class LimGen<T>
where T: IComparable {
public T t1;
public T t2;
// Construtor
public LimGen(T _t1, T _t2){
t1 = _t1;
t2 = _t2;
}
public Max(T _t1, T _t2){
if (t2.CompareTo(t1) <>

return t1;

} else {

return t2;

}

}

}

Esta classe irá compilar correctamente. Contudo, se removermos a cláusula where, o compilador irá retornar um erro indicando que o tipo genérico T não contém uma definição para o método CompareTo. Com esta restrição, garante-se que o método CompareTo estará sempre disponível.

3.5. Eventos

Um evento é uma mensagem enviada por um objecto para notificar a ocorrência de uma acção. A acção pode ser causada por intervenção do utilizador ou por qualquer outra situação da lógica de um programa. O objecto que despoleta o evento é denominado por event sender (originador do evento). O objecto que captura o evento e responde ao mesmo é denominado de event receiver (receptor do evento).

Na comunicação de eventos, o objecto que despoletou o evento não sabe que objecto ou método irá capturar o evento. Assim, é necessário um intermediário (um mecanismo do tipo apontador) entre a origem e o destino do evento. A .NET Framework define um tipo especial que proporciona a funcionalidade de um apontador de funções – o Delegate.

3.5.1. Delegates

Um Delegate é uma classe que pode armazenar uma referência para um método. Ao invés das outras classes, um Delegate possui uma assinatura e pode armazenar referências apenas para métodos que possuam a mesma assinatura (que receba o mesmo número de parâmetros e do mesmo tipo).


Enquanto os Delegates podem ser utilizados para outros fins, a sua principal utilidade tem a ver com a interligação entre objectos que geram eventos e métodos que os capturam e tratam. A declaração de um Delegate é suficiente para definir uma classe do tipo Delegate. A declaração define a assinatura e o CLR (Common Language Runtime) trata da implementação. A seguir temos um exemplo de uma declaração de um Delegate:

public delegate void DelegateDeUmEvento(object sender, EventArgs e);

A assinatura normal de um evento define um método que não retorna valor nenhum, recebe um parâmetro do tipo Object (que referencia a instância do objecto que despoletou o evento) e outro parâmetro derivado do tipo EventArgs que armazena os dados do evento.

EventHandler é um tipo de Delegate pré-definido que representa especificamente um método de tratamento de um evento que não gera dados. Se tivermos a necessidade de criar um evento que gere dados, temos que definir o nosso próprio tipo de dados do evento e, ou criar um delegate cujo segundo parâmetro seja do tipo de dados criado por nós, ou então usar a classe delegate com EventHandler pré-definido e substituir o nosso tipo de dados do evento pelo tipo de dados genérico definido no EventHandler de defeito.

3.5.2. Como responder a um evento

Para responder a um evento é necessário efectuar dois passos:

  • Criar um método que responda ao evento. Este método tem de possuir a mesma assinatura do delegate do evento:
    public void button1_click(object sender, EventArgs e) {
    // Implementação do método
    }
  • Adicionar uma referência indicando qual o método que trata o evento
    this.button1.Click += new System.EventHandler(this.button1_Click);

Desta forma, quando o evento ocorrer, o método especificado como aquele que trata o evento, será chamado e executará o código que implementa.

3.5.3. Como despoletar um evento

Quando se pretende despoletar um evento são necessários, pelo menos, três passos:

  • Criar um delegate:
    public delegate void OMeuEventHandler(object sender, EventArgs e);
  • Declarar um evento:
    public event OMeuEventHandler OMeuEvento;
  • Invocar o delegate dentro de um método quando é necessário despoletar o evento:

    OMeuEventHandler handler = OMeuEvento;
    EventArgs e = new EventArgs();

    if (handler != null) {
    // Invoca o delegate
    handler(this, e);
    }

De notar que, em C#, é necessário que se verifique se o EventHandler é nulo antes de o chamar.

3.6. Atributos

Atributos descrevem um tipo, método ou propriedade de uma forma que estes possam ser programaticamente acedidos através de Reflection (Reflexão). Alguns dos cenários comuns onde se usam atributos incluem:

  • Especificar que previlégios de segurança uma classe requer;
  • Especificar que previlégios de segurança estão proibidos para reduzir riscos de segurança;
  • Declarar capacidades como, por exemplo, suporte para serialização;
  • Descrever características da assembly, fornecendo um título, descrição e informação de copyright.

[assembly: AssemblyTitle("Executável da Aplicação.")]
[assembly: AssemblyDescription("Um software que faz coisas.")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("Easytronic,Lda.")]
[assembly: AssemblyProduct("App v1.2")]
[assembly: AssemblyCopyright("Copyright © 2006 by Easytronic,Lda., Lisboa, Portugal, EU. All rights reserved.")]
[assembly: AssemblyTrademark("App™ is a trademark of Easytronic,Lda., Portugal, EU.")]

Os atributos fazem muito mais do que descrever assemblies para outros programadores, elas podem inclusivamente declarar requisitos ou capacidades. Por exemplo, pode-se definir características relacionadas com Globalização, se está acessível a código COM, entre outros:

[assembly: AssemblyCulture("")]
[assembly: ComVisible(false)]
[assembly: Guid("8a19a392-aa02-4b2d-a784-7da16cfbbee8")]
[assembly: AssemblyVersion("1.2.0.0")]
[assembly: AssemblyFileVersion("1.2.0.0")]

Ou ainda, para permitir que uma classe seja serializada, é necessário adicionar o atributo Serializable a essa classe:

[Serializable]
class UmaClasse {

}

Sem este atributo, a classe do exemplo acima não seria serializável. Da mesma forma, o código seguinte utiliza atributos para declarar que necessita de aceder ao ficheiro C:\boot.ini. Devido a este atributo, o código em execução vai lançar uma excepção anterior ao acesso ao ficheiro, se não tiver previlégios suficientes para aceder ao ficheiro:

using System;
using System.Security.Permissions;

[assembly:FileIOPermissionAttribute(SecurityAction.RequestMinimum, Read=@”C:\boot.ini”)]
namespace ExemploDeclarativo {
class Classe1 {
[STAThread]
static void Main(string[] args) {
Console.WriteLine(“Olá Mundo!”);
}
}
}


3.7. Reencaminhamento de Tipos (Type Forwarding)

O Reencaminhamento de Tipos é uma nova funcionalidade da .NET Framework 2.0 e não é mais do que um atributo (implementado através de TypeForwardedTo) que nos permite mover um tipo de uma assembly (Assembly A), para outra assembly (Assembly B), mas executado de uma forma que não seja necessário recompilar os clientes que consomem a Assembly A. Após um componente ser finalizado e consumido por aplicações cliente, é possível utilizar o Reencaminhamento de Tipos para mover um tipo de uma assembly para outra e depois reenviar o componente actualizado para as aplicações clientes, que estas continuarão a funcionar sem necessidade de serem recompiladas.


O Reencaminhamento de Tipos funciona apenas para componentes referenciados por aplicações existentes. Quando se recompila a aplicação, têm de existir referências apropriadas para todos os tipos utilizados nessa aplicação.


Para executar Reencaminhamento de tipos, devem seguir-se os seguintes passos:

  • Adicionar um atributo TypeForwardedTo à assembly de origem;
  • Cortar a definição do tipo da assembly de origem;
  • Colar a definição do tipo da assembly de destino;
  • Recompilar ambas as assemblies.

O código seguinte demonstra a declaração de um atributo utilizado para mover o TipoA para a biblioteca LibDestino:

using System.Runtime.CompilerServices;
[assembly:TypeForwardedTo(typeof(LibDestino.TipoA))]


Próximo artigo: Conversões Entre Tipos

Wednesday, April 25, 2007

MAIS UMA INTRO À .NET FRAMEWORK 2.0 - Parte II

Artigo anterior: MAIS UMA INTRO À .NET FRAMEWORK 2.0 - Parte I

2. Tipos por Referência

Mais uma vez, é importante referir que um dos mais comuns erros no início da programação em .NET tem a ver com confundir Tipos de Valor ou de Referência com passar argumentos para métodos por Valor ou por Referência. Volto a frisar que são conceitos diferentes e para o qual qualquer pessoa deve ter cuidado.

Os Tipos por Referência armazenam o endereço da sua informação na stack e também são conhecidos como apontadores (pointers). E o que quer isto dizer? Quer dizer que os dados propriamente ditos estão armazenados na heap, mas existe uma referência à sua localização na stack (um apontador), uma área da memória que está acessível mais rapidamente pelo código em excução.

O ambiente de execução gere a memória armazenada na heap utilizando um processo denominado Garbage Collection (“Recolha de Lixo”), que consiste em ir libertando memória periodicamente, eliminando objectos à medida que estes deixam de ser referenciados.

Uma vez que os Tipos por Referência representam o endereço de memória de um pedaço de informação em vez da informação propriamente dita, assignar uma variável por referência a outra variável por referência terá como resultado a cópia do endereço de memória e não dos dados em si, como seria o caso dos Tipos de Valor. Neste caso, ficaríamos com duas variáveis a apontar para os mesmos dados.

2.1. Tipos por Referência Nativos

Existem cerca de 2500 Tipos por Referência incluídos por defeito na .NET Framework, sendo que todos os tipos que não derivam de System.ValueType são Tipos por Referência. Os Tipos por Referência abaixo são os mais comuns, sendo que muitos outros Tipos por Referência derivam destes:

· System.Object. Este é o tipo mais genérico existente na .NET Framework. Qualquer tipo pode ser convertido para este Tipo.
· System.String. Este é um dos tipos mais utilizados na .NET Framework e serve para armazenar dados de texto.
· System.Text.StringBuilder. Armazena dados de texto de uma forma dinâmica.
· System.Array. É utilizado para arrays de dados sendo a classe base para todos os arrays.
· System.IO.Stream. Trata-se de um buffer para operações de Input/Output de ficheiros, dispositivos e rede. Trata-se de uma classe base abstracta.
· System.Exception. É utilizada para tratar excepções. Esta classe gere sobretudo excepções de Sistema, sendo que excepções específicas de tarefas em execução herdam deste tipo.

2.2. Strings e Construtores de Strings (StringBuilders)

Os Tipos são mais do que contentores de informação e fornecem meios para manipular os dados, através dos seus membros. O Tipo System.String disponibiliza um conjunto de membros para trabalhar com texto. O próximo pedaço de código mostra como se pode efectuar uma rápida operação de substituição de texto:

string s = “Texto que é para substituir!”;
Console.WriteLine(s);
s = s.Replace(“é para substituir”, “foi substituido!”);
Console.WriteLine(s);


Este pedaço de código iria produzir o seguinte resultado:

Texto que é para substituir!

Texto que foi substituido!


Contudo, objectos do tipo System.String são imutáveis, o que significa que qualquer mudança a uma string vai fazer com que o ambiente de execução crie uma nova string e abandone a anterior, de uma forma invisível para o programador.


string s = “Esta ”;
s += “operação vai “;
s += “criar 5 “;
s += “strings diferentes “;
s += “em memória!”


O código acima, utilizado várias vezes por muitos programadores com experiência em .NET, na realidade cria 5 strings diferentes em memória. Apenas a última string terá uma referência para os dados, sendo que as quatro anteriores serão descartadas no processo de Garbage Collection.
Esta operação é, portanto, menos performante que utilizar os métodos Concat, Join ou Format da Classe String ou ainda a Classe StringBuider.

A opção pela Classe StringBuilder é a mais eficiente e flexível, porque permite criar strings dinâmicas (mutáveis). O construtor de defeito da Classe cria um buffer de 16 bytes que se expande à medida que vai necessitando, embora seja possível especificar um tamanho mínimo e máximo:


System.Text.StringBuilder sb = new System.Text.StringBuilder();
sb.Append(“Esta ”);
sb.Append(“operação vai “);
sb.Append(“criar apenas “);
sb.Append(“uma string “);
sb.Append(“em memória!”);
string s = sb.ToString();
Console.WriteLine(s);


Outra funcionalidade importante da Classe String é que esta sobrepõe os seus operadores aos da Classe System.Object:

· Adição: +
· Igualdade(comparação): ==
· Desigualdade (comparação): !=
· Atribuição de valor: =

2.3. Criar e Ordenar Arrays

Um array não é mais que uma sequência de apontadores para valores de dados armazenados em memória, ou seja, um conjunto de apontadores de referência para variáveis do mesmo tipo.
Os arrays são declarados utilizando os nomes dos tipos seguidos de parênteses rectos ([]) como parte da declaração da variável. O próximo bloco de código, por exemplo, cria um array e ordena-o:


int[] ar = {3, 4, 1, 2 };
Array.Sort(ar);
Console.WriteLine(“{0}, {1}, {2}, {3}, ar[0], ar[1], ar[2], ar[3]);


De notar que, em C#, quando se pretende aceder ao primeiro elemento do array, ou seja, ao elemento em primeiro lugar no índice do array, se deve utilizar o índice 0 (ar[0]). No caso acima, a declaração do array foi acompanhada pela definição dos valores que o array deveria conter, o que não é obrigatório. Aliás, a grande utilidade dos arrays reside exactamente no facto de se poder utilizar arrays para criar uma referência para dados que serão gerados ou calculados mais á frente, garantindo à partida (com a criação do array) que estas referências estão agrupadas.


int[] numeros = new numeros[3];
for (int i=0;i<3;i++) {
   numeros[i] = i+1;
}


2.4. Streams

As streams são utilizadas para ler e escrever para o disco e comunicar através de uma rede. Existem vários tipos de streams, nomeadamente:

· FileStream, para ler e escrever a partir de ficheiros.
· MemoryStream, para ler e escrever a partir da memória.
· StreamReader, para ler dados de uma stream.
· StreamWriter, para escrever dados para uma stream.

Estas streams derivam de System.IO.Stream, sendo que streams para aceder a recursos de rede podem ser encontradas no namespace System.Network.Sockets e streams de cifragem/decifragem encontram-se no namespace System.Security.Criptography.

As classes mais simples são o StreamReader e o StreamWriter, que nos permitem ler e escrever em ficheiros de texto. Pode-se passar o nome do ficheiro como parte do construtor, permitindo assim a abertura do ficheiro com apenas uma linha de código. De relembrar que, após o processamento do ficheiro, deve ser executado o método Close para que o ficheiro não permaneça trancado. Por exemplo:


using System.IO;

StreamWriter sw = new StreamWriter(“texto.txt”);
sw.WriteLine(“Olá Mundo!”);
sw.Close();

StreamReader sr = new StreamReader(“texto.txt”);
Console.WriteLine(sr.ReadToEnd());
sr.Close();


Mais adiante, num artigo sobre Input e Output, falaremos mais em detalhe destas operações.

2.5. Utilização de Excepções

Excepções são eventos inesperados que interrompem o decurso normal de execução de um programa. Por exemplo, se um programa está a ler um ficheiro a partir de uma localização na rede e o utilizador desliga o cabo de rede ou pura e simplesmente a rede falha por algum motivo, o ambiente de execução irá lançar uma excepção. Isto faz sentido na perspectiva em que não existem condições para que o programa continue a ler o ficheiro a partir da rede.

Contudo, as excepções não devem causar que o programa deixe de funcionar completamente. Ao invés, as excepções devem ser previstas e a sua captura deve ser planeada, para que se possa definir convenientemente como o programa deve agir em resposta a essa excepção.


using System.IO;

try {
   StreamWriter sw = new StreamWriter(“texto.txt”);
   sw.WriteLine(“Olá Mundo!”);
   sw.Close();
} catch (Exception ex) {
   Console.WriteLine(ex.Message);
}


Neste exemplo, o ambiente de execução iria tentar executar o código incluído no bloco Try{ } e se encontrasse uma excepção, iria executar o código incluído no bloco Catch{ }. Caso não encontrasse nenhuma excepção, o programa iria continuar na linha seguinte ao bloco Catch{ }, ignorando-o.

Qualquer que fosse a excepção gerada, ela iria ser capturada pelo bloco Catch{ }, mas é possível (e desejável) que se utilize as várias classes de excepção existentes na .NET Framework para permitir uma arquitectura de manuseamento de excepções mais eficiente e performante. É ainda possível que cada programador defina as suas próprias classes de manuseamento de excepções para maior detalhe na identificação e tratamento das mesmas, criando classes derivadas de System.ApplicationException ou de System.Exception. No entanto, é considerado boa prática efectuar a definição de excepções próprias derivadas de System.Exception, uma vez que se acabou por verificar que derivar as excepções a partir de System.ApplicationException não trazia valor acrescentado.

Para além dos blocos Try{ } e Catch{ }, o manuseamento de excepções também suporta um terceiro bloco, denominado de bloco Finally{ }, que executa após a execução do bloco Try{ } e de qualquer Catch{ } que tenha sido executado, independentemente de ter ou não sido encontrada alguma excepção. Assim, a utilização do bloco Finally{ } faz sentido para executar tarefas de limpeza ou qualquer outra tarefa que tenha de ser executada sempre, haja ou não excepções a tratar.


using System.IO;

StreamWriter sw = new StreamWriter(“texto.txt”);
try {
   sw.WriteLine(“Olá Mundo!”);
} catch (Exception ex) {
   Console.WriteLine(ex.Message);
} finally {
   sw.Close();
}


De notar que a declaração do objecto StreamWriter foi movida para fora do bloco Try{ } no exemplo anterior, porque o bloco Finally{ } não consegue aceder a variáveis declaradas no âmbito do bloco Try{ }. Isto faz todo o sentido, porque, dependendo do local onde as eventuais excepções ocorrem, as declarações de variáveis dentro do bloco Try{ } podem ainda não ter sido executadas.

É fortemente recomendado que todo o código, com excepção da declaração de variáveis, deva ser executado entre blocos Try{ }/Catch{ }/Finally{ } para melhorar a experiência do Utilizador e também para melhorar a depuração (debugging) das aplicações.

De seguida (no próximo artigo) irei falar-vos de Classes.