static marca um membro que pertence ao tipo, não a um objeto. Um campo de instância existe uma vez por objeto; um campo static existe uma vez, e pronto. Um método de instância executa em um objeto (order.Ship()); um método static executa no tipo (Math.Round(x)) e não precisa de objeto nenhum.
Campos static: uma cópia, compartilhada
A forma mais clara de ver a diferença é um contador. Cada Order tem o seu próprio Id, mas a contagem de pedidos criados até agora precisa ficar em algum lugar compartilhado.
Saída:
Ana #1, Ben #2, Chen #3
Orders created: 3
Membros static são acessados pelo nome do tipo: Order.Created, não a.Created. C# não permite chegar a um membro static por meio de uma instância (erro CS0176), o que deixa óbvio, no ponto da chamada, que o valor é compartilhado.
Métodos static
Um método static não tem this. Ele trabalha com os parâmetros e só com outros membros static. Isso o torna a forma natural para auxiliares que calculam algo a partir das entradas:
Saída:
61.38 EUR
7
O próprio Main é static: o runtime o chama antes de existir qualquer objeto.
O erro CS0120: código static usando membros de instância
Como o Main é static, iniciantes muitas vezes esbarram nesse erro assim que adicionam um segundo método:
class Program
{
int visits;
void Greet() { Console.WriteLine("Hello"); }
static void Main()
{
Greet(); // error CS0120: An object reference is required for the non-static field, method, or property 'Program.Greet()'
visits++; // error CS0120: An object reference is required for the non-static field, method, or property 'Program.visits'
}
}
Um método static não tem objeto, então não existe um visits para ele incrementar. Duas soluções, e a certa depende do que o membro é:
- O membro realmente é um dado por objeto: crie um objeto e use-o.
var app = new Program(); app.Greet(); - O membro não precisa de estado de objeto: marque-o como
static.static void Greet() { ... }
Marcar tudo como static para o erro sumir funciona em um programa minúsculo e vira estado global em um programa real. Decida perguntando se o dado pertence a um objeto ou ao programa inteiro.
A direção inversa é sempre permitida: métodos de instância podem chamar membros static à vontade.
Classes static
Uma classe declarada como static só pode conter membros static e nunca pode ser instanciada. Ela é um grupo com nome de funções e constantes.
Saída:
77
100
True
O compilador garante o contrato: um membro de instância dentro de uma classe static é o erro CS0708, e new nela é o erro CS0712. Math, Console, File, Path e Enumerable são todas classes static. Métodos de extensão também precisam ficar em uma classe static.
Construtores static e static readonly
Um campo static cujo valor precisa de mais que uma expressão pode ser definido em um construtor static, que o runtime executa uma vez, antes de o tipo ser usado pela primeira vez:
Saída:
Before first use
ShippingRates initialized
5.50
3
2026-01-01
readonly significa que o campo só pode ser atribuído no inicializador ou no construtor static. Depois disso ele não pode ser reatribuído, embora o objeto para o qual aponta ainda possa ser mutável, e é por isso que o dicionário é exposto como IReadOnlyDictionary.
const vs static readonly
Os dois dão um valor com nome compartilhado pelo programa inteiro, e diferem no momento em que o valor é fixado:
const | static readonly | |
|---|---|---|
| Valor fixado em | tempo de compilação | tempo de execução, uma vez |
| Tipos permitidos | números, char, bool, string, enums, null | qualquer tipo |
| Implicitamente static | sim (não dá para escrever static const) | declarado static explicitamente |
| Copiado para os assemblies que o usam | sim | não, lido do campo |
Usável em cases de switch e argumentos de atributos | sim | não |
A linha da cópia importa para bibliotecas: se o assembly A declara public const int MaxItems = 50; e o assembly B o usa, B contém o literal 50. Mudar A para 100 não faz nada por B até B ser recompilado. Para valores que podem mudar entre versões, prefira static readonly.
using static
Uma diretiva using static (C# 6) importa os membros static de um tipo, para que você possa chamá-los sem o nome do tipo:
Saída:
Area: 28.27
40
Ela se lê bem em código com muita matemática. Use com moderação em outros lugares: um WriteLine ou Parse solto esconde de que tipo o método vem.
Estado static e threads
Um campo static é compartilhado por todas as threads do processo. Incrementá-lo a partir de várias threads ao mesmo tempo perde atualizações, porque created++ é uma leitura, uma soma e uma escrita, e duas threads podem intercalar esses passos. Use Interlocked.Increment(ref created) para contadores, ou um lock para qualquer coisa maior. Aplicações web são o lugar onde isso costuma morder: cada requisição executa em alguma thread, então um campo static que guarda "o usuário atual" é compartilhado por todos os usuários.
Mais uma sutileza: em uma classe genérica, cada tipo construído tem os próprios campos static. Cache<int>.Count e Cache<string>.Count são dois campos diferentes.
Quando usar static
Bons usos:
- Auxiliares puros que calculam um resultado a partir dos argumentos: formatação, conversões, validação.
- Constantes e dados de consulta somente leitura compartilhados pelo programa.
- Métodos de fábrica, como
TimeSpan.FromMinutes(5)ou o seu próprioTemperature.FromCelsius(21). - Métodos de extensão.
Maus usos:
- Estado global mutável (o usuário atual, um carrinho de compras, configurações que mudam). Ele acopla todo código que mexe nele e faz os testes dependerem das sobras uns dos outros.
- Qualquer coisa que você vá querer trocar nos testes, como um relógio, um banco de dados ou um enviador de e-mail. Chamadas static não podem ser substituídas por uma versão falsa; uma instância passada ao construtor pode.
Perguntas frequentes
O que static significa em C#?
Um membro static pertence ao próprio tipo, e não a algum objeto. Existe uma única cópia de um campo static, não importa quantos objetos existam, e um método static é chamado pelo nome do tipo (Math.Max(3, 7)) sem criar uma instância. Um método static não tem this, então não pode usar campos de instância diretamente.
O que é uma classe static em C#?
Uma classe declarada como static só pode conter membros static e nunca pode ser instanciada (new nela é o erro CS0712). Ela agrupa funções relacionadas que não precisam de estado de objeto, como Math, Console ou File. Métodos de extensão precisam ser declarados em uma classe static.
Como resolver "An object reference is required for the non-static field, method, or property"?
Esse é o erro CS0120: um método static, muitas vezes o Main, está usando um membro de instância. Crie um objeto e chame o membro nele (var app = new Program(); app.Run();), ou marque o membro como static se ele não precisar de dados por objeto.
Qual a diferença entre const e static readonly em C#?
Uma const é uma constante de tempo de compilação (números, strings, bool, enums) cujo valor é copiado para todo assembly que a usa. Um campo static readonly é atribuído uma vez em tempo de execução, no inicializador ou no construtor static, e pode ser de qualquer tipo, como um DateTime ou uma lista. Use const para valores que nunca vão mudar, e static readonly para todo o resto.
Quando usar static em C#?
Use static para código que só trabalha com seus parâmetros (auxiliares puros, como um formatador de preços), para constantes e caches realmente globais ao programa e para métodos de fábrica. Evite static para qualquer coisa que guarde estado por usuário ou por requisição, ou que você vá querer substituir nos testes: estado mutável compartilhado é difícil de testar e inseguro entre threads sem lock.