views:

108

answers:

3

In our ASP.NET MVC project, we have an HtmlHelper extension method to generate a static google map.

public static MvcHtmlString StaticMap(this HtmlHelper helper, string address, string alt, int width, int height, int zoom)
{
    var src = new Uri("http://maps.google.com/maps/api/staticmap?markers=size:mid|color:red|{0}&zoom={1}&size={2}x{3}&maptype=roadmap&sensor=false".FormatInvariant(Uri.EscapeUriString(address), zoom, width, height));
    var href = new Uri("http://maps.google.com/maps?f=q&source=s_q&hl=en&q={0}".FormatInvariant(Uri.EscapeUriString(address)));

    var img = new TagBuilder("img");
    img.MergeAttribute("src", src.ToString());
    img.MergeAttribute("alt", alt);

    var link = new TagBuilder("a") { InnerHtml = img.ToString() };
    link.MergeAttribute("href", href.ToString());

    return MvcHtmlString.Create(link.ToString());
}

For this new project, we are also trying to keep all code analysis rule on. Now, obviously, Visual Studio Code analysis states that we should remove the helper parameter because it is not being used.

That made me wondering if an extension method should always make use of the extended object and if it does not, then maybe it shouldn't be an extension method.

Does someone has a link to a guideline or an explication that would help me decide?

+12  A: 

If you're not using the helper object, you'r not extending anything and there's really no reason not to make this a regular static method in your own namespace.

Justin Niessner
Right. By making it an instance method, you're basically tying yourself to having an instance of `Foo` when you don't need one. If you don't already have an instance in your particular block of code, you're going to have* to create one just to call a method that has nothing at all to do with the instance created! What a waste. (*Of course, the static extension method could be called directly using `FooExtensions.Bar(null, [rest of args here...])`
Anthony Pegram
Well no, he's clearly extending something whether its used in that extension or not. Calling code can now do something with such an object that it couldn't before, so its extended. That previously existing members aren't used in doing so is an implementation detail.
Jon Hanna
+1  A: 

Why did you make this an extension method to begin with? I'd say that this is not something you should do. I can't think of a good reason for it.

Bryan
Mostly because that's pretty much all you see in ASP.NET MVC: Extension methods for everything
Pierre-Alain Vigeant
@Pierre-Alain, while I'm the one person to so far argue that such an approch is reasonable, I don't see that as a good reason. It sounds a bit too much like "well, everyone else did it...".
Jon Hanna
I don't want to say my approach is good, nor that I'm stating an argument, but when learning, you tend to copy what you see. Then it is when you ask question about what everybody else is doing that you truly learn.
Pierre-Alain Vigeant
@Pierre-Alain, yes I'm just saying that while IMO there are some points in favour of the approach, that's not one of them. Its reasonable to be one one "side" in a difference of opinion on a design issue like this, but still disagree with points made in that sides favour. Indeed, its not being prepared to do that that stops it being a potentially informative discussion, and just becomes an opaque row that doesn't inform.
Jon Hanna
+4  A: 

The distinction to the using code between an extension method and a static method is purely conceptual; one relates to an object of a particular type in the same manner as an instance method, and the other does not.

This being so, I would ask the question, "does considering this operation to be upon an HtmlHelper object make sense?". This in turn becomes "does considering HtmlHelper objects to provide this operation, make sense?". If I answered "yes" to that question, I would consider an extension method to be a reasonable approach, whether I used an HtmlHelper object or not. Conversely, if I answered "no" to that question, I would consider an extension method to be an ill-advised approach, even if the HtmlHelper object did get used.

The code-analysis does a better job at analysing code-use than concepts, so it gives a warning here. It's imperfect, and indeed there are other cases where ignoring a parameter is reasonable (maintaining compatibility either with a previous version or an interface being the best case, "reserved for future use" being a more arguable case).

It's notable that in some languages (well, C++ for one) you can leave a parameter name out precisely to mean "this parameter is in the signature, but won't be used". I quite like this, as it is true that generally not using a parameter is a bad sign, so it's nice to have a means to indicate you were deliberate in this.

Edit:

Another justification. Imagine you had an extension method on a class that did use the relevant object. Now imagine you realise you can make it more reliable, efficient, or otherwise "better" for some value of "better" by re-writing in such a way that you are no longer using the object. Should you not be allowed to keep the extension method now that you've improved it? Should you be forced to stay with the inferior version?

Jon Hanna
"Should you not be allowed to keep the extension method now that you've improved it?" - My answer to that question is usually yes, you should **not** keep the extension method. By removing a parameter, I've made code simpler and clearer. E.g. If I managed to alter `void doItFast(this SpeedBooster booster, string IT)` to `void doItFast(string IT)`, I would want to change my calls from `instBoost.doItFast(instIT)` to `doItFast(instIT)`.
Brian
@Brian. That's a breaking change if the method is public. It's potentially a lot of changes, and hence a lot of potential bugs, if it isn't public. As a rule I'd probably change to the cleaner version too, but not if it was public and I didn't have control over all using code, as I wouldn't consider it as justifying a breaking change.
Jon Hanna
@Jon: I agree with all your points, though those are more about changes in general than this specific change. That said, I felt, perhaps incorrectly, that you were using this as a thought experiment to justify using an extension method on a class that did not use the object, to support your argument that it was OK in general to use extension methods when they seem like the object provides the operation. I was disagreeing with this point.
Brian
@Brian. Oh, I'm still saying that's a reasonable use of an extension method, and I am using it to back that up - assuming that it was a reasonable extension method the first time though. In all, I think the question to ask is not "does this belong with these other bits of code?" as "does this belong with what this class semantically does?" In the case in the original question, "does this belong in HtmlHelper, or has it nothing to do with what HtmlHelper is **for**?".
Jon Hanna