Skip to main content

C# 2.0 - Membros estáticos

Antes de mais, vamos definir uma ou duas regras de vocabulário para o artigo:

  • Static - Vou referir-me a esta palavra reservada como a sua tradução literal "Estático". Exemplo: classe estática, método estático...
  • Instance Members - Esta é uma tradução pobre, eu sei, mas refere-se a membros que apenas estão acessíveis utilizando uma instância de uma determinada classe. Chamar-lhes-ei de membros de instância
  • Isolation Frameworks - Refiro-me a frameworks que ajudam a criar mocks, fakes e stubs para unit testing. Irei traduzir como Frameworks de Isolamento

Em C# 2.0 foi adicionada uma nova funcionalidade denominada por classes e membros estáticos. Quando digo membros refiro-me a métodos, propriedades, eventos, etc. Eu tenho algumas opiniões sobre esta palavra reservada que irei partilhar ao longo do artigo, mas vamos começar por olhar para o que podemos fazer com esta feature. Vejamos este código

void Main()
{
Person p
= new Person();
p.Name
= "test";
Person.NumberOfBrains
= 1;
}

public class Person
{
private static int _NumberOfBrains = 0;
private string _Name = null;

public static int NumberOfBrains
{
get
{
return _NumberOfBrains;
}
set
{
_NumberOfBrains
= value;
}
}

public string Name
{
get
{
return _Name;
}
set
{
_Name
= value ;
}
}
}

Acima estão definidas duas propriedades, sendo que uma é estática. No método Main criamos uma instância da classe Person e atribuímos um valor à propriedade da instância (Name). Olhemos agora para a parte do código que menciona "Person.NumberOfBrains = 1". Esta é a propriedade estática que criámos na classe. Reparem como o acesso é feito ao nível da classe em vez de ao nível da instância. Esta é uma diferença que pode parecer subtil, mas significa que apenas vai existir um valor na propriedade NumberOfBrains, independentemente das instâncias da classe Person criadas, mas existirá sempre um valor diferente na propriedade Name que pode variar por cada instância. Resumindo, membros estáticos são utilizados quando queremos que um determinado membro esteja acessível independentemente de haver ou não uma instância de uma classe.

Como é claro o exemplo acima não é o melhor, mas dá para perceber a ideia. Ao utilizarmos esta feature temos que ter algumas regras em mente:


  • Não é possível aceder a membros de instância através de métodos estáticos. Na minha classe Person eu não poderia aceder à propriedade Name dentro de um método estático, por exemplo. Para isso teria de passar a instância por parâmetro.

public class Person
{
public string Name; //Esta variável não é static

private static void SetName() //Este método é static
{
Name
= "asd"; //Não podemos aceder a um membro não static sem uma instância!
}
}


  • Não é possível ter membros de instância em classes estáticas. 

public static class Person
{
public string Name; //Não é possível definir este membro numa classe static
}


  • Não é possível instanciar uma classe estática. O que é que estaríamos a instanciar exatamente?
  • Ao utilizarmos classes estáticas perdemos alguns conceitos OO, tais como herança e composição (não podemos implementar interfaces).

Esta última é uma das razões pelas quais eu não gosto de escrever o meu código estático e tento sempre reduzir ao máximo os sítios onde utilizo. Alguns dos argumentos são:



  • Código estático não é facilmente testável e substituível por mocks. A maioria das frameworks de isolamento (excepto algumas super-frameworks) utilizam herança para criar implementações próprias chamadas  mocks e stubs que facilitam a testabilidade do código. Se estivermos a utilizar uma classe estática, não podemos utilizar estas frameworks e não podemos isolar partes do nosso código que utilizam essa classe estática.
  • Classes estáticas não permitem boas formas de tornar o código extensível. Não é possível herdar de uma classe estática e não podemos obviamente fazer override a métodos/propriedades estáticas. Isto destrói completamente a extensibilidade da classe.
  • Já vi desculpas como "desta forma não precisamos de instanciar um objecto". Para mim isto não faz sentido só por si. Claro que não temos que instanciar, mas o que ganhamos disso? Apenas em casos muito específicos é que eu não quereria utilizar uma instância de um objecto e perder todas as vantagens do polimorfismo e da herança.
  • Classes e membros estáticos tornam-se um íman de mau código quando cometemos o erro de os deixar crescer ao ponto de passarem a definir regras de negócio. Antes de darmos por isso temos que passar uma miríade de argumentos ou criar um DTO para manter a lista de argumentos razoável.

Como trabalho diariamente com código muito baseado em classes estáticas, falo por experiência própria neste assunto. Já vi toda uma camada de acesso a dados baseada em classes estáticas, e isto não é algo do qual me orgulho. Claro que testá-la era um pesadelo, e apenas possível manualmente de forma razoável, pois era impossível criar mocks de dependências estáticas.

À parte disto penso que há algumas situações bastante válidas onde código estático é extremamente útil:


  • Para conter métodos auxiliares que não envolvam lógica de negócio (um exemplo é a classe Convert do ..NET)
  • Para manter uma lista de variáveis globalmente acessíveis ou serviços para toda a aplicação. Utilizo isto para não ter que instanciar um objecto e injectá-lo em todo o lado (sim, aquela desculpa). Claro que faço isto apenas para código muito, muito, muito simples. Normalmente, esta classe estática global apenas contém propriedades (geralmente interfaces) para não perder testabilidade.
  • Métodos estáticos têm uma grande importância em C# com a introdução dos Extension Methods. Veremos isso mais tarde.
  • Para conter métodos que não fazem grande sentido numa classe específica.

A minha abordagem é: tentar sempre manter o comportamento e a lógica da aplicação longe de membros estáticos. Porque estaríamos a utilizar uma linguagem OO como o C# se não utilizamos as suas capacidades OO? Claro que haverão sempre situações em que usar membros estáticos é melhor ou mais fácil, por isso é uma óptima adição na linguagem!

Continuem a programar!

Comments

Popular posts from this blog

The repository's repository

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

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

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

My simplest and most useful type

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