Also, any api/library that lets you create an object that is unusable because specific methods to set up values haven't been called yet is very error prone.
Also, any api/library that lets you create an object that is unusable because specific methods to set up values haven't been called yet is very error prone.
The problem with optional parameters is they work by call site rewriting. If I have some library method where I give an optional parameter a default value, and a program is built against it - if I later change the default value, this will not be propagated to the program until it's recompiled - it'll continue to use the old default.
They could probably fix this by implementing overloads behind the scenes, rather than rewriting the call site though. If Java does ever implement this, I hope they do it that way.
That is: public void DoSomething(string required, string optional = null) is actually DoSomething(string, string), but at compile time a call to DoSomething("foo") is compiled to DoSomething("foo", null).
Hence optional parameters are not recommended for an external API.
Personally, I don't really see that as much of a problem. Having run into that issue before, I think the compiler catches it as as an error because its an ambiguous method signature. Instead, you have to pass something like: "DoSomething("foo", (string)null)" which, in that case, defeats the purpose of the optional parameter altogether.
The real issue is if you have DoSomething(string what, autoRetry=true) and you change to DoSomething(string what, autoRetry=false) - DLLs compiled against the old version will call with true even if the authors wanted the default behaviour.
That said, in this circumstance my method sounds more like a hack and yours the cleaner, correct route.
public void A() { A(1); }
public void A(a) { A(a, 2); }
public void A(a, b) { A(a, b, c); }
public void A(a, b, c) { /* do something */ }
Or, worse, public void A() { A(1, 2, 3); }
public void A(a) { A(a, 2, 3); }
public void A(a, b) { A(a, b, 3); }
public void A(a, b, c) { /* do something */ }
It wastes several lines and could be much more simply expressed, to the developer as: public void A(a = 1, b = 2, c = 3) { /* do something */ }
Regarding the issue you have, I can certainly see that as an actual issue and it should probably be dealt with; though, the CLR developers probably had to decide between two opposing and frustrating viewpoints: "What if they want the old default? After all, they did compile against it, knowing that that's what we set it to default"; and, "Maybe they just want the computer to figure out what's best for them..." As a library designer, you'll now have to consider the possibility that they're using the old version of your library. Realistically, they most likely will compile their program again instead of in-place replacing your dll. It's safer that way.All of that said, when I usually use optional parameters, I generally set them to null. You cannot set them to, for example, a new instance of an object, they have to be static values; so, if someone uses a default parameter, my code sees: "oh, it's null. Now I'll use my default parameter that I set internally, because it came from a configuration file and I couldn't set that as a default parameter anyway given their constraints", and I don't have the problem you mention at all.
Example:
public void A(a = null, b = null, c = null) {
a = a ?? new AParam();
b = b ?? SomePropertyFromAConfigFile;
c = c ?? DBNull.Value;
/* do something */
}
The ?? syntax makes this practically the recommended route, I think. (a ?? b is C# shorthand for (a != null? a : b) ). I'm happy, the people using my library are happy, and the issue that you describe doesn't exist in my code because of this. Of course it does make it a bit awkward for some kinds of primitives, for example; but, if they don't want to provide a parameter to my library, that parameter being provided as 'null' seems the most accurate way for my library to receive it anyway.That's what the Builder pattern[1] is for, avoiding the instantiation of incomplete objects.
I've thought about the appropriateness of that pattern to Python, and so far what I've come up with is merely to use optional named parameters for private/simple classes and Builder classes for public/brittle/robust classes/APIs.