Optional argument corner cases, part two
(This is part two of a series on the corner cases of optional arguments in C# 4. Part one is here. Part three is here. This portion of the series was inspired by this StackOverflow question.)
Last time we saw that the declared optional arguments of an interface method need not be optional arguments of an implementing class method. That seems potentially confusing; why not require that an implementing method on a class exactly repeat the optional arguments of the declaration?
Because the cure is worse than the disease, that's why.
First off, we saw last time that one method on a class could implement two methods of two different interfaces:
interface IABC
{
void M(bool x = true);
}
interface IXYZ
{
void M(bool x = false);
}
class C : IABC, IXYZ
{
public void M(bool x) {}
}
If we require the implementation to repeat the default values then the methods cannot be implicitly implemented; at least one will have to be explicitly implemented.
However, that's a rare case and we can probably dismiss it as unlikely. There are other problems though.
Suppose you have an interface in your code:
public interface IFoo
{
void M(int x, string y, bool z);
}
and a hundred different classes that implement it. Then you decide that you want to say
public interface IFoo
{
void M(int x, string y, bool z = false);
}
Do you really want to have to change a hundred different declarations of the implementing method? That seems like a lot of burden to put on the developer, but we could do it.
Suppose we did. Now suppose that interface IFoo is not defined in your source code, but rather is provided to you by a third party. The third party makes a new version of their interface that has a default value for the parameter z. Now when all of their thousands of customers recompile, all of those thousands of customers have to update their source code to match the default parameter! Requiring this redundancy causes the introduction of a default value to become a potentially large compilation-breaking change.
That's bad. But wait, it gets worse. Suppose you have this situation. IFoo is provided by third party FooCorp. Base class Bar is provided by third party BarCorp:
public class Bar
{
public void M(int x, string y, bool z) { ... }
}
Note that Bar does not implement IFoo. You wish to use both FooCorp and BarCorp code in your assembly, where you say:
class Mine : Bar, IFoo
{
}
(The compiler allows this because M on Bar is a member of Mine, and therefore Mine implicitly implements IFoo.M.)
Now FooCorp ships a new version of their assembly with the default value on z. You recompile Mine and it tells you that no, you can't do that because Bar doesn't have a matching default value for z. But you didn't write Bar! What are you supposed to do, call up BarCorp and ask them to ship you a new assembly just because FooCorp -- their competitor -- added a default value to a formal parameter of an interface? What are you supposed to do if BarCorp refuses? The method isn't even virtual, so you can't override it. The solution is ugly:
class Mine : Bar, IFoo
{
public new void M(int x, string y, bool z = false)
{
base.M(x, y, z);
}
}
Best to simply not require the redundancy in the first place.
Next time: making an argument optional does not change the signature of the method
(This is part two of a series on the corner cases of optional arguments in C# 4. Part one is here. Part three is here.)
Comments
Anonymous
May 12, 2011
The comment has been removedAnonymous
May 12, 2011
The comment has been removedAnonymous
May 12, 2011
"Now suppose that interface IFoo is not defined in your source code, but rather is provided to you by a third party. The third party makes a new version of their interface that has a default value for the parameter z. Now when all of their thousands of customers recompile, all of those thousands of customers have to update their source code to match the default parameter! Requiring this redundancy causes the introduction of a default value to become a potentially large compilation-breaking change." It already is, because the author of the library providing IFoo cannot assume that implementor will be C#. If it is VB, then changing the value of an optional argument is already a major breaking change (and has been since VB7, IIRC). For the same reason, changing the names of method parameters has been a breaking change for a long time now - C# doesn't care, but VB does, and as a library author you cannot assume which one the API client will use.Anonymous
May 12, 2011
I'm with Carl - these examples don't seem that contrived to me. As I recall, C# has single inheritance on classes for a reason. You can also look at a typical business application and see a lot of similarly-named methods on a lot of unrelated classes - how many classes have a method called Create() for example? There are situations where optional arguments / default parameters are useful, but they should definitely be used with care.Anonymous
May 12, 2011
@CarlD, @John - I did not imply Eric's particular example were contrived. I was curious how and if hypothetical and contrived scenarios play a part in the compiler design process.Anonymous
May 12, 2011
The comment has been removedAnonymous
May 12, 2011
It's cool to see how a very basic IT lesson (try to avoid redundant information) can impact even something complex like a compiler.Anonymous
May 12, 2011
@Mike - If a post (not this post, an arbitrary one) is based on "real-world" scenarios, I would not ask if "real-world" scenarios played a part in the derivation of the post since that would be tautological. Although I did not mean to imply Eric's examples were contrived, they appear artifical (at least superficially), so one could call them "contrived".Anonymous
May 13, 2011
Patrick, you are not making any sense.Anonymous
May 13, 2011
A tad wordy perhaps, but he does make sense. Do optional parameters create a bigger problem than they solve?Anonymous
October 05, 2011
The comment has been removed