Req16: Modules that don’t auto-import their contents; extension methods in classes.


[This post is part of a series, “wish-list for future versions of VB“]


 


IDEA 1: Shared Classes. We should allow “Shared Classes”. Their members are all implicitly shared (like a Module), they can’t be instantiated (like a Module), but their contents won’t be auto-imported:


    f1() ‘ allowed


    f2() ‘ disallowed


    M1.f1() ‘ allowed


    C2.f2() ‘ allowed


 


    Module M1


        Public Sub f1()


        End Sub


    End Module


 


    Shared Class C2


        Public Sub f2()


        End Sub


    End Class


 


IDEA 2: An <DontImportModuleContents> attribute on modules. We should have an attribute, Microsoft.VisualBasic.DontImportModuleContentsAttribute, which prevents the contents of a module from being auto-imported.


 


 


Provisional evaluation from VB team: Instead of these ideas, there’s an easy workaround that works already: just stick your shared methods inside a Class rather than a Module. The only thing you don’t get from this is that the compiler doesn’t enforce the discipline that everything is shared — but this is a very small shortcoming, and not worth changing the language for.


The workaround doesn’t work for extension methods, though: they have to go inside Modules. And their presence inside Modules is awkward because they then appear in intellisense as normal methods (as well as extension methods). Well, that’s a shame, but it could be mitigated a little by an IDE fix — having them appear on the “All” tab rather than the “Common” tab. And it doesn’t seem worth changing the language for this one reason.


 


IDEA 3: Allow extension methods to be defined in classes. Currently all extension methods must be defined in modules. But imagine if we could define them in classes. There are two possible ways to do this:



  1. Maybe the extension methods are only visible within the current class (and subclasses). This would be a nice way to scope extension methods.

  2. Or maybe the extension methods would be visible everywhere. This would be a workaround for the above issue (of extension methods appearing in intellisense).

 


This Idea3.1 seems like it might be useful, but also definitely confusing. What do you think? Have you ever wanted more control over the scope of extension methods?

Comments (8)

  1. Kevin Ryall says:

    If you do nothing else from this list, please, PLEASE do this one. Surely it’s a simple change to make…

    Auto-imports from modules are useful in some rare situations (and I wouldn’t want to lose the functionality), but in the VAST majority of cases it’s a nuisance – and has led to modules being banned by our coding standards.

    I (almost) never want to see unqualified methods as it makes the code less readable, but I regularly want to use static classes – so we are reduced to using regular classes with all static methods and a private constructor to achieve this as the cost of auto-imports is too great. I know the effect is the same, but a regular class doesn’t prevent instantiation or instance methods, and more importantly, doesn’t communicate intent the way a static class /module does.

    It probably simpler to use modules rather than introducing a new Shared Class syntax – that’s not a problem as it’s as likely to be understood by our programmers either way. As long as there is an attribute we can apply to prevent automatic importing (which can remain the default), I’ll be happy.

    The requirement to put extension methods in modules makes this even worse – forcing these methods to appear in intellisense everywhere is a terrible experience. The solution is NOT an IDE change to work around this weird VB insistence on polluting the global namespace – it’s changing modules / static classes so that global importing is not forced on us. Being able to define the extension methods directly in classes would be nice, but it’s peripheral to the main issue – which is the completely unnecessary tying of global importing to static classes / modules.

    I have to stress again that the workaround is not a good one – not because the compiler won’t enforce the static nature of the class, but because the INTENT of the class is not obvious. It’s MUCH more important to make it immediately obvious to readers what the code is likely to mean than to get the compiler enforcing the contract. The workaround (which we use everywhere as we have no choice) gives us nothing in terms of communicating intent.

    Personally, I regard this as a no-brainer – it would be a shame not to implement it.

  2. I much prefer the C# way of handling extension methods – i.e. placing them in classes.

    In general modules seem like a big throwback and before extension methods I hadn’t used them in any VB.NET projects, they just seem to clutter the namespace they are in.

  3. Dzonny says:

    <DontImportModuleContents> might be useful and it’s not change to the language – just tell the IDE what to do with the attribute.

    Shared Classes are confusing a little bit when we have modules.

    I also highly appreciate possibility of defining extension method in any class/structure (or even interface, enum).

  4. Tim says:

    I absolutely agree with Kevin here! The auto-import functionality of modules is an annoyance. I understand that you want to keep the default behavior as it is, but PLEASE give us any way to deactivate the auto-import somehow. <DontImportModuleContents> or shared class, i do not care as long as we have any way to achieve this.

    A little hint for those who want to use extension methods without bloating your global intellisense: Decorate the module containing your extension methods with the following attribute: <System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)>

    This way neither the module not the caontaint methods will show up in global intellisense, but the extension methods stell appear on instances as desired.

  5. Tim says:

    Uh, sorry for the typos in my last message. The last sentence should be:

    "This way neither the module nor the contained methods will show up in global intellisense, but the extension methods still appear on instances as desired."

  6. Héctor says:

    Agreed with the other people, the auto-import functionality of modules is an annoyance, and prefer static classes from C#.

    If I want to do this, I use not inheritable classes with static members, but it’s a pain extension methods can only be declared inside modules.

  7. Joshua Frank says:

    I generally agree with all the previous posters.

    The main scenario I run into is that I want to put an extension method on a class that I don’t have the source to, such as String, so I could do, say:

    mystring.TreatAsHtmlAndParse()

    But I only want to do this in some subset of my project that deals with HTML strings, and I only want this extension method to work within that subset.  The current requirement to put all extension methods in a module, combined with the fact that modules are project-wide at a minimum and auto-importing, means that this extension method attaches to all strings everywhere, and this is annoying.  Better would be to let me extend String ONLY within the scope that I specify.

    On a related note, I have a few extension methods that extend Object.  I know some people frown strongly on this, but it works for me.  Unfortunately, there is the limitation that if the variable is actually typed As Object, the extension method isn’t callable, because of the mixup with late binding.  It’s not a huge deal, but like would be a little better if this were resolved.

  8. Kyralessa says:

    I’m very much in favor of shared classes.  This would make the syntax analogous to C# static classes.  It always throws me for a loop when I go back to VB .NET and find that I can’t make a class Shared; it just seems like one of those things I should be able to do.