Summary#
A static method is called on the type, not on a value:
class Money {
public decimal Amount;
public static decimal Round(decimal value) {
return Math.Round(value, 2);
}
}
decimal RoundAPrice() {
return Money.Round(19.999m); // on the TYPE — there is no Money instance here
}Reach for it when the operation is about the type but not about any particular value of it — a conversion, a validation, a calculation over its arguments.
Signature#
static <Return> <Name>(<params>) { … }
public static <Return> <Name>(<params>) { … }static goes after the visibility word, as in C#. It applies to methods only.
Description#
A static method has no this#
That is the whole of the rule, and everything else follows from it. There is no instance, so there is nothing for an instance field to be read from:
class Money {
public decimal Amount;
public static decimal Doubled() {
return Amount * 2m; // ✗ 'Amount' belongs to an INSTANCE
}
public static decimal Half() {
return this.Amount / 2m; // ✗ `this` has no meaning in a static method
}
}Take the value as a parameter instead — which is usually what the method wanted anyway:
class Tax {
public static decimal Net(decimal gross, decimal rate) {
return gross / (1m + rate);
}
}What a static method CAN see of its own class#
Its other static methods and its const values, both by bare name — the same rule as C#:
class Rate {
public const decimal Standard = 0.25m;
public static decimal Apply(decimal amount) {
return amount * (1m + Standard); // a const, by bare name
}
public static decimal ApplyTwice(decimal amount) {
return Apply(Apply(amount)); // another static, by bare name
}
}
[Test]
void Statics_Reach_Statics_And_Consts() {
Assert.Equal(125m, Rate.Apply(100m));
Assert.Equal(156.25m, Rate.ApplyTwice(100m));
}An instance method may call its class's static methods the same way — it simply does not pass its this along:
class Line {
public decimal Gross;
public decimal Net() {
return Tax.Net(Gross, 0.25m);
}
}Call it on the type, never through a value#
The two directions are both compile errors, and each says which spelling to use:
var m = new Money { Amount = 1m };
m.Round(2.5m); // ✗ 'Money.Round' is static — call it as `Money.Round(…)`
Money.Amount; // ✗ 'Money.Amount' is an instance member — access it through an instanceRefusing the first is C#'s rule too, and it is worth the strictness: m.Round(…) reads as though the method can see
m, and it cannot — the receiver would be evaluated and discarded.
Statics inherit#
A static method declared on a base class is callable on a derived one, like any other inherited member:
class Shape {
public string Name;
public static decimal Zero() { return 0m; }
}
class Circle : Shape {
public decimal Radius;
}
decimal ZeroThroughTheSubclass() {
return Circle.Zero(); // Shape declares it; Circle inherits it
}It cannot be virtual, override or abstract, and combining them is a compile error. Those words choose a body
from the receiver's runtime type, and a static call has no receiver to choose by.
Static methods overload#
Exactly like instance methods — a name maps to a set, and the call site picks by the arguments. See Method overloads for the rule.
class Fmt {
public static string Of(decimal d) { return d.ToString(); }
public static string Of(decimal d, string unit) { return d.ToString() + " " + unit; }
}Fields cannot be static#
static applies to methods only. A static field would be mutable state shared by every application running in
the host process, with no per-app copy to reset — so it is refused, in as many words:
class Counter {
public static decimal Total; // ✗ cannot be `static`
}There are three things people reach for it for, and each has its own answer:
| what you want | reach for |
|---|---|
| a fixed value the whole program shares | const — already static, and needs no modifier |
| a value that belongs to one object | an ordinary field |
| state that outlives a request | an entity — stored, per-app, and secured |
For the same reason there is no static constructor: it would exist to initialize static state, and there is none.
See also#
- class methods — instance methods, and the member-body surface a static method shares
- Method overloads — how a call site picks from a set of same-named methods
- Classes — fields,
const, and what a class is - type visibility (public / internal) —
public,privateandprotected, which combine withstatic