1

I have an IResult<TResult> generic class and subclasses Success<TResult> and Failure<TResult> used for representing successful/failed results and associated information. To cut down on the cumbersome repetition of generic parameters all over the place (it's easy enough when TResult is just a class, but gets very ugly if there's nested generics), I have an extension method to help with building:

public static class SuccessExtensionsClass
{
    public static BuildSuccess<TResult> Success<TResult>(this TResult result)
    {
        return new Success<TResult>(result);
    }
}

Now I can do something like:

TResult blah = new TResult();

then

return blah.BuildSuccess();

So far so good. I don't need to supply the generic type to the extension method, as desired.

However, if I were to use the syntax:

BuildSuccess(blah)

I get a compiler error about the missing generic type parameter. Now, on the other hand, if I change it to:

BuildSuccess<TResult>(blah)

It can't find the method until I specify the class as with a normal static method:

SuccessExtensionClass.BuildSuccess<TResult>(blah)

works fine, as does:

SuccessExtensionClass.BuildSuccess(blah)

Why? Or, more specifically, what motivated this choice? The compiler has all the same information available in both cases, as the error about a missing generic type when called as BuildSuccess(blah) makes very clear. Is it just a design choice that extension methods can only be called without being qualified with their class name when in extension syntax? Is this for purposes of code clarity alone or is there another reason I'm not thinking of?

As a follow-up, why wouldn't the static syntax be subject to the same rule as instance method syntax, where the compiler will (only) resolve the name if you have explicitly imported the namespace?

edit for hopefully more clarity:

When in instance syntax, I don't have to call the extension method as:

blah.SuccessExtensionsClass.BuildSuccess()

but in static syntax I do need to call it as:

SuccessExtensionsClass.BuildSuccess(blah)

Is this simply to enforce clarity?

David
  • 169
  • 5
  • Unless Eric Lippert (or another compiler-team member) decides to chime in, or some anecdotal blog post is found giving a CLEAR explanation of exactly why this was done, this question is probably primarily opinion-based since the rest of us can only give best guesses, not concrete answers. – David L Sep 21 '16 at 19:14
  • Indeed, and my hope in posting is that either Eric or someone who knows the rationale will indeed chime in. – David Sep 21 '16 at 19:17
  • Sorry, I'm confused... You're using the extension method properly, but you want to use it improperly `BuildSuccess(blah)`? That would only work where `BuildSuccess` is defined. Extension methods aren't universal methods. –  Sep 21 '16 at 19:21
  • According to [this article about extension methods](https://msdn.microsoft.com/en-us/library/bb383977.aspx) when invoking the extension method "normally" the IL generated is invoking them like static methods. Seems fair to assume that your non-extensiony-invocation is not being caught by the compiler, and your static invocations are just being ignored since they are already in the intended form. – stephen.vakil Sep 21 '16 at 19:23
  • `ClassName.MethodName()` used to be the only way to reach static methods. The newer extension method syntax allows for a briefer form, but the compiler effectively translates to the old form. – spender Sep 21 '16 at 19:24
  • " Extension methods aren't universal methods." - They are when called in instance syntax, just not when called in static syntax. But the two are equivalent under the hood, and the compiler clearly has the same information in both cases. So the question is why it works in one syntax and not the other, given that both are equivalent somewhere under the hood. – David Sep 21 '16 at 19:26
  • It's not the same information in both cases. In one case it's, "I have an instance of TResult and I want to invoke Success on it" and in the other it's, "I'm a class and I want to invoke the Success method with an argument of type TResult" -- the former is more specific and constrained. The latter would require searching a wider range of potential meanings. – stephen.vakil Sep 21 '16 at 19:35
  • In you example, extension method name is `Success`, so `SuccessExtensionsClass.BuildSuccess` should not work at all, as you do not have such method. Can it be that method actually named not `Success`, but `BuildSuccess` (same name as its return type)? – user4003407 Sep 21 '16 at 19:39
  • _"more specifically, what motivated this choice? The compiler has all the same information available in both cases"_ -- primarily opinion based. Re: the compiler's available information, yes and no. Keep in mind, while the source code may include the information, the compiler implementation may or may not be in a position to use it. There are many steps to compiling code, and it may well be e.g. that parsing an expression that looks like an instance invocation of an object provides the information needed to efficiently handle extension methods, while your alternative syntaxes don't. – Peter Duniho Sep 21 '16 at 19:39
  • Further food for thought: What if your class where the build is called from had a method `BuildSuccess(TResult result)`? Now the invocation you desire becomes ambiguous or, worse, prone to unintended effects, because it might be your extension method or it might be your class method. When you call `TResult.BuidlSuccess()` there is no potential scope ambiguity. – stephen.vakil Sep 21 '16 at 19:45
  • 1
    @PeterDuniho It definitely has the necessary information, because the compiler is able to detect the generic type parameter is missing. Once you add the parameter, it all of a sudden "can't" find the method. – David Sep 21 '16 at 20:40
  • @stephen.vakil - Agreed, it could of course cause an ambiguity, but it should not be hard to decide on rules for that, should it? Maybe similar static method in class (or parent class if in a subclass) takes priority, followed by extension method. I suppose maybe the easiest answer is that C# needs to allow the instance syntax to use unqualified method names for it to be anything other than terribly clunky, while the same is not true for static syntax. – David Sep 21 '16 at 20:45
  • _"It definitely has the necessary information"_ -- "definitely"? That's a strong word lacking any evidence. In fact, the behavior you described in that comment support the possibility that the compiler depends on a specific structure of the expression in order to process the method call, and that the information is _not_ in fact available to the compiler at the point in time it needs it to do what you want. (If only I had a penny for every time a person who has never done X is so sure that the people who did do X could've done something that, for whatever reason, didn't do...) – Peter Duniho Sep 21 '16 at 20:59
  • 1
    If what you are suggesting were true, and the compiler could not resolve the method with the type parameter, it also certainly could not properly resolve the method *without* the type parameter. But it can, and does. How do you propose that the compiler could possibly not have the requisite information when it previously had the requisite information, and the only change is the addition of *more* specific information? That makes no sense whatsoever. If `BuildSuccess(blah)` resolves properly, there is no information-based reason that the more specific `BuildSuccess(blah)` would not. – David Sep 21 '16 at 21:17
  • And like I said above, I'm very open to the idea that this is a design decision, but unless I'm very much missing your point can you explain why adding a type parameter would plausibly cause the compiler to be unable to resolve to the matching specific method when it was able to resolve without the type parameter? – David Sep 21 '16 at 21:19

1 Answers1

1

Extension methods are static methods that can be called with the first argument written before the method name, separated from it by a dot, so it looks as if it was an instance method:

blah.BuildSuccess()

In this example, blah is really the first argument to the static method. This is what extension methods are about.

You seem to desire another syntax, namely:

BuildSuccess(blah)

Of course, that syntax is legal already if you happen to be inside the class that defines the method. But if you want that syntax to apply more generally, what you seek is not extension methods but simply using static directives.

So drop the this modifier:

namespace Your.Relevant.Name
{
  public static class SuccessHelperMethods
  {
    public static Success<TResult> BuildSuccess<TResult>(TResult result)
    {
      return new Success<TResult>(result);
    }
  }
}

Then with:

using static Your.Relevant.Name.SuccessHelperMethods;

at the top of your compilation unit (code file), this syntax will be legal:

BuildSuccess(blah)

Note: The using static directive will not allow this syntax if you keep the this modifier (extension method declaration).

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
  • 2
    I didn't know about the `using static` directive! Thanks, that's very interesting, and while it isn't *quite* the same use case it covers the reasonable ones. On the other hand, it naturally creates the followup question: why doesn't `using static` allow extension methods? If I can call `SomeStaticClass.SomeExtensionMethod(obj)`, why can't I `using static SomeStaticClass` and then call `SomeExtensionMethod(obj)`? That seems very arbitrary. It almost seems like extension methods should not permit static syntax at all for consistency. – David Sep 21 '16 at 22:05
  • This was also a major surprise to me when the `using static` feature came (in C# 6, from Visual Studio 2015). We will have to ask the language designers; it was a deliberate design decision they took. See e.g. [Why can't I call an extension method as a static method when using static import?](http://stackoverflow.com/questions/38984853/) – Jeppe Stig Nielsen Sep 22 '16 at 09:50
  • Didn't know about this either. What is the point of `using static`? It seems to reduce clarity to me while offering no obvious benefit other than a few less characters typed on the keyboard. – stephen.vakil Sep 22 '16 at 15:54
  • @stephen.vakil That is a valid point. But if you use `Math.Pow`, `Math.Abs`, `Math.Cos` etc. a lot, then an expression ("formula") can be easier to type and grasp without all the `Math.` stuff. Also, if you have a lot uses like `type.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static)`, then it is nicer to have just `type.GetMembers(Public | NonPublic | Instance | Static)`. Those are just examples. But in reality, I do not use it much, so it is not the most revolutionary feature. – Jeppe Stig Nielsen Sep 23 '16 at 06:53