• Supersonic Tumbleweed (unregistered)

    Nice way to confuse co-developers. I want to see the VS code that located the definition, and picked different one on different invocations.

  • Vilx- (unregistered)

    Not sure why the extension methods got the flak this time. This would work perfectly the same way if these were proper instance methods. And it would work this way in pretty much every language that offers method overloading. C#, C++, Java - you name it. And there wouldn't be any warnings either. TRWTF are the shitty naming practices.

    Remy: I'd argue that no compiler should allow this kind of ambiguity. It's not that extension methods are a problem, but that the compiler should be more strict about ambiguity.

  • Newbie (unregistered)

    Hint: Adjust the project settings. You can "treat warnings as errors". You can select a different Rule Set for Code Analysis. You can modify your Rule Set as you like, switching between Warning, Error, Info, None.

  • someone (unregistered)

    Non-C# dev here, just writing to confirm:

    The quirk is having the "this" instance passed as the first parameter, rather than defining the function as an actual method of ScreenScraper?

  • Pjrz (unregistered)

    Being an extension method has nothing to do with it. C# allows you do this with standard non-static methods.

    But there also doesn't appear to be any ambiguity when I tested it (in VS 2015): The compiler appears to go for the first method (the one without optional parameters) if it can. Otherwise, if any optional parameters are supplied in the call then it will obviously have to go for the overload.

    It IS confusing though, so not a great example of programming.

  • (nodebb) in reply to Vilx-

    The problem is not defining 2 functions with duplicate signatures like this; the problem comes when you try to call it. The C++ linker will not know which version of the function to use and effectively say "Forget it!" and not generate an executable.

  • SK (unregistered) in reply to Vilx-

    Such ambiguities are illegal in C++, and I'd expect any sane language to throw errors (not just warnings) in those cases.

    $ g++ test.cpp test.cpp: In function ‘int main()’: test.cpp:9:11: error: call of overloaded ‘bar()’ is ambiguous foo.bar(); test.cpp:9:11: note: candidates are: test.cpp:4:7: note: int Foo::bar() int bar(); test.cpp:5:7: note: int Foo::bar(int) int bar(int v = 0);

    As for Java, it doesn't have optional parameters (aka. default parameter values).

  • Sally Flynn (unregistered)

    "the difference between optional parameters and an overloaded function is just the quantity of boilerplate"

    This is wrong: optional parameters are NOT a substitute for method overloading. When using optional parameters, there's only one method, and the call sites are rewritten by the compiler to automagically insert the default values. COmpare this to method overloading, where there's more than one method with the same name but different argument lists.

    From the viewpoint of C#, the result may be the same, but when you call these methods from a language that doesn't support optional parameters, the difference is very significant..

  • (nodebb) in reply to someone

    Passing an object to a function is fine and done all the time; the problem is the arguments can work out to be the same for each definition. e.g.

    strArray = GetMessages(Scraper1);
    

    This code could be calling either version of the function. Normally the programmer would have to define an unambiguous naming to allow the compiler to determine which one you want.

    If you do not control

    ScreenScraper
    , adding a function to the definition might not be possible. There is a flag in .NET inheritance to prevent a class from being used as a parent class of an derived class, so you might be prevented from extending the class that way as well.

  • bla (unregistered)

    Firstly, extension methods are great, and the issue here has nothing to do with them. Secondly, this may be a pretty stupid way to code it, but I don't see why the compiler should not let you do it. Which method will be used is defined in the spec (hint: it's choosing the method the way one would expect).

  • Damien (unregistered)

    Yep, perfectly "fine". By the C# spec, if you don't supply any values for the optional parameters, it has to pick the overload with no optional parameters as the better member. No ambiguity. There's no way to call the second method accepting the defaults for both optional parameters, but that's not necessarily a problem

    I.E. you may either have to specify neither of these values or both, so the only time you want to call the second version is when you have a timeSpane (and accept a default timeInterval), and timeInterval (and accept a default timeSpane) or want to specify both.

  • Little Bobby Tables (unregistered)

    Aha! I see TRWTF:

    timeSpane
    
  • Mr. TA (unregistered)

    The C# language team takes great care to analyze these edge cases. I'm sure if you ask them they will present perfectly valid reasons why they chose to allow this instead of throwing a compilation error. They have to consider many factors, like introducing a breaking change to a language, different assemblies having extension methods which have different authors, etc. C# is not the problem here, the developers are. PEBKAC.

  • TVJohn (unregistered) in reply to Nutster

    But the signatures aren't duplicate. The compiler will know which to call by the arguments. And if you try to call it with a further different set of arguments you'll get a compile time error. I'm not entirely sure I see a problem here.

  • Brian (unregistered) in reply to Sally Flynn
    This is wrong: optional parameters are NOT a substitute for method overloading. When using optional parameters, there's only one method, and the call sites are rewritten by the compiler to automagically insert the default values. COmpare this to method overloading, where there's more than one method with the same name but different argument lists.

    Spoken like a Real Programmer. Regardless of what happens at the compiler level, the point is that, from a software-development perspective, both are a form of design efficiency that allows a developer to say "I'm ok with ignoring these parameters and letting the underlying code do what it wants with them." Yes, one could certainly get off in the weeds talking about the various benefits and drawbacks of each approach, but that's not really the point of the article, now is it?

    But speaking of the article... how could that code possibly compile? There should have been errors about ambiguous function calls.

  • (nodebb)

    The C# language team takes great care to analyze these edge cases. I'm sure if you ask them they will present perfectly valid reasons why they chose to allow this instead of throwing a compilation error. They have to consider many factors, like introducing a breaking change to a language, different assemblies having extension methods which have different authors, etc. C# is not the problem here, the developers are. PEBKAC.

  • (nodebb)

    As a C# developer, I concur that this is ambiguous from a human standpoint. The compiler will handle it just fine, but if a human reader needs extra steps to confirm how the compiler will handle it (and maybe confirm again later, and then another human reader has to check later, etc.) then it shouldn't be written as-is. Unless there is a dire shortage of words to use in method names, give two methods that do different things different names.

    The biggest problem isn't code that won't compile. The biggest problem is code that does compile but a human reader has to spend unnecessary extra time figuring out what it does.

    Addendum 2018-08-23 09:27: Or, to clarify: Give different names to methods that do the same thing with the same parameter list except for tacked-on optional parameters.

    Addendum 2018-08-23 09:33: Adding to the confusion, the second developer realizes that the first developer had some odd one-off reason for creating the ambiguous methods, but doesn't know what that reason was. That creates uncertainty, because the second developer realizes that the first developer had intentions which weren't clearly communicated and did messy things. Now the second developer will spend more time questioning everything.

    In a clean code base you can focus primarily on what you're changing. Once you see weird stuff you lose confidence and start looking for more weird stuff, which you usually find. Then you have to overcome momentary grief and despair as you realize that the clean, maintainable code base you always hoped to work on one day isn't this one, but maybe the next one or the one after that.

  • doubting_poster (unregistered)

    Agreed with others, extensions have nothing to do with the WTF. Remy should really learn some C# before throwing shade ;)

  • jburka (unregistered) in reply to someone

    Nope. That's the valid syntax for one declares an extension method in c# -- it allows you to add methods to existing classes without subclassing them.

    Honestly, to me the only WTF here is that the overloaded extension methods weren't colocated in the source. Sure, it could be cleaned up in a number of ways, but it's not like overloading isn't a normal technique in c#.

  • (nodebb)

    I'm not a C# programmer, but it sounds like its disambiguation rules essentially make the 2nd argument of the 2nd definition required, even though it's written as if it's optional.

    However, writing it this way means you can remove the first definition and you don't have to modify the second definition. Perhaps the history is that the second definition was written first, then they added an overload to special-case when neither of the optional arguments are supplied.

    Overloads like that could also be useful in languages that allow you to use named optional arguments, like Python, so that you can supply either optional argument without the other. Does C# do this?

  • Pierre Lebeaupin (unregistered)

    Yikes! Fortunatlely, in Swift the compiler will throw an error in case of such an ambiguous call: http://wanderingcoder.net/2014/10/21/swift-thoughts/ (search for "named parameters", and yes I ought to add anchors to that post).

    Which reminds me I ought to write an explanation for how useful actual named parameters (as opposed to just C++ "you can omit the last n") are for unit testing.

  • (nodebb)

    @Sally Flynn Optional arguments and overloading are both just syntactic sugar for defining functions with different names, and calling the variant that you want.

  • GF (unregistered) in reply to TVJohn

    The Signatures aren't the same, but since the second argument is optional, how is the compiler supposed to make the difference between someone calling the first function (which doesn't use the optional) and a user calling the second function but who keeps the optinal argument to its default value?

  • (nodebb)

    I vote it's a crappy language. Compare with R (yes, I know that's comparing chocolate cake with frisbees), where there's only one instance of a function, and optional args are just that: optional. If the user doesn't supply them, the defaults are used. If the user does supply them, user input is used. No confusion at all.

  • Donald Knuth (unregistered)

    It's perfectly cromulent in my book. In general I'd go with a same named extension where both methods share code, the extra parameter version just does something else before or after calling the shared code.

    Extensions are great when you don't want to (or cannot) inherit from a class, just tack a little bit on.

  • (nodebb)

    In this case it isn't the languages fault, it is the developer. I usually stand with this: Optional Parameters and Method Overloading are two ways to handle the same thing; pick one and stay consistent. If the overloaded method is not doing essentially the same thing, then it should not be the same name.

    There are many cases where overloading methods are necessary and fully understandable. A generic convert method for some disparate date always outputting some other common type. It's parameter list may very well be overloaded to take many different types but in each case the result will be the common type. No reason to make a lot of different names here. It's functionality is understood, it takes any form of input and converts it to X, always, every time, with the same expectation of success or failure.

    That is the purpose of overloads and it does make things clean and easily readable. It is up to the developer to be sure he keeps it clean and easily readable and immediately understandable without having to dig into it each time.

    The original though, the compiler will pick the first match which is the one without the additional parameters if none are supplied.

  • Ian P (unregistered)

    Submitter here: Visual Studio did go to the first definition when no arguments were supplied. It is fairly smart about that. As I recall, there were no warnings about ambiguity in the build output. Apparently Visual Studio was happy about it. Props to Visual Studio for making it easy to quickly rename all of the instances of the function with the optional arguments to something unique.

  • (nodebb)

    The C# compiler is the real WTF.

    Enough said ^^^^ right here ^^^^

  • ooOOooGa (unregistered)

    Heading off into the weeds a bit more...

    Optional parameters and function overloading are definitely not the same thing. Function overloading is actually more powerful. The ability of optional parameters can be completely duplicated by function overloading. Just have the less specified function call the more specified function by supplying the default values.

    But there are things in function overloading that can't be done with optional parameters. The ones that I can think of that don't deserve to be featured on this site involve an algorithm that can accept different data types as input. With function overloading I can define function signatures for each supported data type, implement one of them in full, and have the others do data conversion on the input and call the fully implemented function. With only optional parameters I can't define the different function signatures for the different data types.

    Except that I am working in PHP which allows functions to have untyped inputs. So I would only have to write the signature once and either rely on type juggling to do the type conversion, or do explicit type checking on the input and do the data type conversion myself. But now I am getting back to code design that deserves to be featured here.

    So of course TRWTF is PHP.

  • (nodebb) in reply to Carl Witthoft

    Nope it's like comparing chocolate cake with a pile of poop. Having fewer features means R is an inferior language.

  • (nodebb) in reply to Barry Margolin

    Yes it does, although only added recently.

  • Richard Walker (unregistered)

    First time I've ever seen someone accuse the C# compiler team of a WTF. Generally when someone looks at an aspect of the C# design and says "WTF," the problem is the developer not fully understanding the full implications of what they think the "correct" implementation should be.

    But I'm sure Remy has taken a thorough look at the deep internals of how extension methods are implemented and isn't wrong on this one. /sarc

  • Perri Nelson (unregistered) in reply to Barry Margolin

    Not with extension methods, which can be defined in different static classes.

  • Paul Neumann (unregistered)

    This isn't even the most egregious example of extension method fail I've seen. Consider extension methods with the same signature from different referenced assemblies. Provided the extensions are in different namespaces, Visual Studio will never give you a warning about it. If you have a using statement for both namespaces, which one gets linked?

    ! The first using statement determines the linked reference. I found this out when tidying up the usings in a project.

  • (nodebb)

    The mere fact that this construct leads to a discussion like this indicates that there might be some fault in the language at large. With so many "specialized" constructs in a language that are only used fractions of a percent of the time leads to multiple interpretations of said features.

    All of this is generated from languages that are complicated for the sake of being complicated and non-understandable. Therein lies the greatest WTF!

  • Eugene Po (unregistered)

    C# has clearly defined rules for dealing with such cases, that are described in https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments#overload-resolution

    So it is GetMessages(this ScreenScraper screenScraper) that should end up being used at any dutData.GetMessages() invocation.

    While it is possible that some version of csc.exe is broken, it is far more likely that it is the "Go to definition" command in their Visual Studio version that behaves erratically (the author doesn't claim that different overloads were called - just that "Go to definition" lead to different spots in their codebase).

  • I Saw a Robot (unregistered)

    TRWTF is people who think ambiguity in a program is just fine. Do you want the compiler to call the correct version of the method (or extension), or not?

    Now, since an extension method is just syntactic sugar for an outside call that uses your passed-in "this" to make all method calls, it makes sense for that extension method to have priority over the class method call. But if you have two extension methods that can possibly be called with the same args, can you be sure which one is getting called? I guess in keeping with the above philosophy, the "newest" prototype wins. But if you need VS to tell you which function is actually being called, then maybe it's time to rethink your approach?

  • Wat (unregistered)

    This has nothing to do with class vs extension methods.

    The fact that the author goes on a diatribe about evil extension methods without understanding what they do is TRWTF. It's not relevant to the story at all. The issue is that methods optional parameters count as overloads even though they potentially hide other methods. The following is just as possible within a class definition:

    public void Test() {} public void Test(string option = null) {}

    It will build. Perhaps the compiler should not allow for it, but it does. C++ will allow the same, and some C++ compilers won't even spit out warnings about it.

    There's nothing to see here folks. This is just a case of someone who has never seen C-type languages.

  • Richard (unregistered)

    Whether or not you agree with the behaviour, it is at least documented clearly:

    "If two candidates are judged to be equally good, preference goes to a candidate that does not have optional parameters for which arguments were omitted in the call. This is a consequence of a general preference in overload resolution for candidates that have fewer parameters. "

    docs.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/named-and-optional-arguments#overload-resolution

  • (nodebb)

    I wasn’t aware of that. I’ve just googled a bit and found an article that backs your explanation. This is a WTF imho. It means you should not use optional arguments in public APIs consumed by separate assemblies because if you then change the value of one of those parameters you end up with the other assembly still using the old default values.

  • Simon (unregistered)

    For the record, the same ambiguity exists with var-args methods. In Java, if I have the following methods, distinguished by only the var-args parameter:

    public void myMethod () {
    	System.out.println("var-args");
    }
    
    public void myMethod (String... optional) {
    	System.out.println("no-var-args");
    }
    

    If called with no arguments, the compiler will call the former, rather than calling the latter with an empty array.

  • (nodebb)

    I'm not sure what behavior you were seeing, but your description doesn't gel with my tests.

    If your project has references to two assemblies with the same extension method signature in different namespaces, then everything will be fine as long as you only reference one of the namespaces in a using statement. If both namespaces are referenced by using statements then compiler throws the following error, highlighting the ambiguity;

    CS0121 The call is ambiguous between the following methods or properties: 'Bar.Ext2.Qux(string)' and 'Foo.Ext1.Qux(string)'
    
  • JProgrammer (unregistered) in reply to SK

    Java doesn't have default values, but it does have optional parameters, and those can be abused.

  • Peter Tramo (unregistered)

    I think the point of allowing this is to allow for either of the optional parameters to be ignored. IIRC you can use named parameters which may, for instance, allow you to skip the first one. so, this declaration : void DoStuff(int param1=0, int param2=42) could be called as : DoStuff(1750) or Dostuff(param2=38) So having both parameters optional makes sense here even if you only intend to always provide one of them. Then if you want a no-parameter version doing something different enough, having a true parameter-less separate declaration as in today's WTF makes sense. Well, "makes sense"...wanting this is probably TRWTF but it's a software design issue, not a problem in C#

  • (nodebb)

    That's a WTF I did not know of yet. I had to test it, and voilà, it does exist in VS2015 (tested with both methods in the same class). Fortunately, we have ReSharper which will tell us: “Method with optional parameter is hidden by overload” at the level of "Error". Thank you, Microsoft!

  • Colin Cowie (unregistered)

    The only WTF is the developer being stupid enough to define optional parameters on a method without realising that it cannot in fact be called without supplying at least one of the optional parameters. Defining methods as extension when they could be instance methods is at worst not required and slightly less clear than the alternative. However, there may well have been policy on not touching already defined and completed class source files, or they may have been defined in an assembly to which the programmer did not have access to modify the source for at the time the code was written. It's even possible that the extension methods were defined in a particular namespace within the project to keep use of them local to the namespace, as extension methods will only be available when "using" the namespace the static class containing the extension method is defined within. All perfectly sane reasons for using an extension method instead of an instance one.

  • Alexei Humeniy (unregistered) in reply to someone

    The issue is the second method having all optional arguments so there's no clarification about which method is called then you do the invoke.

  • Namespaces (unregistered)

    I guarantee these two extension methods were in two different namespaces. Namespace A has Extension Method A (without parameters), namespace B has extension method B (with default parameters).

    Thus, if you only import "using" namespace B, then only the extension method with default parameters is 'in scope' and thus it is the one that will be linked in. If you import both namespaces, then the C# rule that the version without default parameters will win comes into play. In SOME of the source files, both or only 'A' was imported, in others, only 'B' was imported which would explain the differences in behaviour.

    Remember that each of these namespaces can be in different assemblies (JAR files in Java speak); so one could not expect the compiler to refuse to compile either assembly -- after all, each assembly is an independent unit

  • (nodebb)

    This is perfectly valid, and expected and has nothing to do with how Extension Methods operate. The version of the method with optional parameters is not 'hidden' either, consider:

    void Main() { Console.WriteLine(Foo()); Console.WriteLine(Foo(baz: 20)); }

    public string Foo() { return "Foo() called"; }

    public string Foo(int bar = 10, int baz = 15) { return $"Foo({bar}, {baz}) called"; }

  • David Mårtensson (unregistered)

    The example probably omits something.

    I believe that the two similar methods might also be in different namespaces and different parts of the code includes different namespaces in which case different methods will be called even without extra arguments.

    If the methods was in the same place the one without optionals will always be choose as its the best match.

    Another option could be if the calls are from different sub assemblies (different modules) where some where compiled with only one method present.

    If you then change the assembly with the definitions (you sometimes can do that without recompiling) you might end up with calls to the "wrong" method.

    But this is developer error, not language error.

    Extension methods, just like macros in C++ is very powerful but can also be used to create stupid code.

    Should we ban all powerful constructs just because you can do stupid things with them we would get very little left ;)

Leave a comment on “Extending Yourself”

Log In or post as a guest

Replying to comment #498766:

« Return to Article