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? ...

Follow up: improving the Result type from feedback

This post is a follow up on the previous post. It presents an approach on how to return values from a method. I got some great feedback both good and bad from other people, and with that I will present now the updated code taking that feedback into account. Here is the original: And the modified version: Following is some of the most important feedback which led to this. Make it an immutable struct This was a useful one. I can't say that I have ever found a problem with having the Result type as a class, but that is just a matter of scale. The point of this is that now we avoid allocating memory in high usage scenarios. This was a problem of scale, easily solvable. Return a tuple instead of using a dedicated Result type The initial implementation comes from a long time ago, when C# did not have (good) support for tuples and deconstruction wasn't heard of. You would have to deal with the Tuple type, which was a bit of a hassle. I feel it would complicate the ...

C# 2.0 - Partial Types

For those of you interested, i found a very interesting list of features that were introduced in C# in  here . This is a very complete list that contains all the features, and i'm explaining them one by one in this post series. We've talked about  Generics  and  Iterators . Now it's time for some partial types . A partial type  is a type which definition is spread across one or more files. It doesn't have to be in multiple separated files, but can be. This is a very simple concept that can give us many benefits, let's see: If a type is partial, multiple developers can work on every part of it. This allows a more organized way of working and can lead to production improvement.  Winforms , for example, generates a partial class for the form so that the client can separately edit other parts it. This way, a part contains information about the design and the other contains the logic of the form. In fact, this is a very spread pattern across .Net. Ent...