The conflicting pieces of advice you've read all make sense in a way, but they all make (different) assumptions or ambiguously phrase things. It's one of those "saying the same thing with different words" types of distinctions.
a method should only be static if it does not modify a state
Note the ambiguity of "modify a state". The following example violates this rule (literally), but it preserves the (figurative) spirit of the rule:
public static void SetUserNameToBob(User u)
{
u.Name = "Bob";
}
This modifies the state of the User
object, so it does in fact "modify a state".
However, this method does not rely on a particular internal state in order to decide it's own logical flow (e.g. u.Name = currentlySelectedDefaultName()
would be a violation as the selected name is a selected state). And I think that's what is meant: no internal state is modified.
[a method should only be static if] its result depends only on the parameters provided to it
Look at the previous item, this one is pretty much saying the same thing. What it means is that this:
public static string currentlySelectedDefaultName;
public static void SetUserNameToBob(User u)
{
u.Name = currentlySelectedDefaultName;
}
should not be static, as the "current default" name is a state, and therefore it should not be a global variable/method.
Think of what happens if two separate threads are running: one of them wants to default to "Bob", the other wants to default to "Jim". They will end up fighting over the value, which is liable to create massive debugging issues and unexpected behavior.
If, however, every thread has its own DefaultNameSetter
object, then they don't fight over the same resource.
However, the most highly voted answer in this post states that static methods should be used whenever possible.
This is sort of enforcing a rule by try/failing. Can we set this method to static?
- Yes
=>
Good! Keep it that way!
- No
=>
This proves that the code is relying on a non-static value somewhere and therefore should not be made static.
To indirectly quote Jeff Goldblum in Jurassic Park, you should not argue the need for doing something merely by proving that it can be done.
Again, the approach is not necessarily (or always) wrong, but it blindly assumes that methods are already written to be as state-agnostic as logically possible, which is simply not the case.
Even if you subscribe to this methodological approach, you can still only apply it when a project is no longer in development. If the project is still in development, current methods may be placeholders for the future implementation. It might be possible to make Foo()
static today, but not tomorrow, if the state-dependent logic simply hasn't been implemented yet.
Many of the answers in this post say that one should do whatever is most logical.
Well, they're not wrong; but isn't this just a minor rephrase from saying "do the correct thing"? That's not really helpful advice, unless you already know when to use statics and when to avoid them. It's a catch 22.
So when should you use statics?
As you've noticed, not everyone agrees on the rule, or at least on how to phrase the rule. I'll add another attempt here, but be aware that this is effectively creating another standard:
Keep that in mind.
Statics are universal truths.
That is the purpose of a global namespace: things that are correct across the entire application layer.
There is a slippery slope argument here. Some examples:
var myConfigKey = ConfigurationManager.AppSettings["myConfigKey"];
This is a very clear cut example. Application configuration inherently implies that the configuration is global to the application, and therefore a statis method is warranted.
bool datesOverlap = DateHelper.HasOverlap(myDateA_Start, myDateA_End, myDateB_Start, myDateB_End);
This method is universally correct. It does not care which dates you're comparing. The method does not care about the meaning of the dates. Whether they're employment dates, contract dates, ... doesn't matter to the method's algorithm.
Note the semantical similarity between contextual and state driven. Both sort refer to a "non universal" state. This proves that contextual differences therefore are state-dependent and not suitable to be made static.
var newPersonObject = Person.Create();
This is a universal truth. The same creation process is used all over the application.
However, this line can get blurred:
var newManager = Person.CreateManager();
var newJanitor = Person.CreateJanitor();
From a technical perspective, nothing has changed. Manager (and janitors) are created the same way all over the application. However, this has subtly created a state (manager/janitor), which slowly but steadily detracts from how universal the truth really is.
Can it be done? From a technical perspective; yes.
Should it be done? That's a matter of whether you're arguing the pure principle, or take into account that compromises need to be made in order to not pointlessly strive for logical perfection. So I would say if it doesn't create a bigger problem than the problem intends to solve.
As the options expand (managers, janitors, accountants, salespeople, ...), the problem grows. For a sufficiently large problem, a factory pattern is desirable.
If you only have two options and you have no reason to suspect that the list of options will grow, you can argue that the static creation methods suffice. Some might disagree, and I see their point too. But I tend to be practical and not overly perfectionist in my approach.