3

A client has a variable declared using a static type - similar to the following test (which compiles in VB):

Dim test As System.IO.File

What is the purpose of this? In my client's code, this variable was not referenced anywhere so I couldn't follow any usage pattern. I would have expected that VB would have a problem with this declaration (as C# does), but since it doesn't I assume that there is some esoteric VB-ish purpose to this?

Dave Doknjas
  • 6,394
  • 1
  • 15
  • 28
  • @CodyGray: No - VB should be warning about declaring a variable of a static type - which appears to have no purpose (this is a compile error in C#). – Dave Doknjas Aug 31 '16 at 17:22
  • 3
    Sorry. It took me a while to understand that `System.IO.File` is a static class, so having a variable of that type makes no sense. – sstan Aug 31 '16 at 17:23
  • 1
    To the downvoters - I think you are being hasty and not really reading the question. – Dave Doknjas Aug 31 '16 at 17:23
  • 2
    Oh, I see. I was confused by your usage of "static type". Normally, when people talk about static types, *especially* in the case of variable declarations, they're making a distinction between static and dynamic types. This was probably the source of @sstan's confusion, too. Now I see that you're talking about instantiating a *static class*, which yes, should be illegal. And is, as far as I know, even in VB.NET. – Cody Gray - on strike Aug 31 '16 at 17:23
  • @CodyGray: Really? I don't think anyone calls the type in a static field declaration a static type. – Dave Doknjas Aug 31 '16 at 17:24
  • @sstan: Wow - you're generating points for an erroneous comment and then for noticing that! – Dave Doknjas Aug 31 '16 at 17:25
  • You are using terminology in ways that I am not familiar with. A "static field" is a variable of a class that is shared between all instances. No, no one calls that a static type. What I mean by static types is *compile-time binding*, as opposed to dynamic/run-time binding. http://stackoverflow.com/questions/1517582/what-is-the-difference-between-statically-typed-and-dynamically-typed-languages (Coments votes do not earn reputation.) – Cody Gray - on strike Aug 31 '16 at 17:26
  • @CodyGray: I think the reason this is confusing is that having a variable declared with a static type is no weird/useless that people aren't even used to the concept - anyway, "why does VB allow this nonsense?". – Dave Doknjas Aug 31 '16 at 17:29
  • You've probably pulled out a skeleton from the closet. You know, remnants from the dark ages ( – Bjørn-Roger Kringsjå Aug 31 '16 at 17:45
  • @Bjørn-RogerKringsjå: Yes, I think so - I write source code converters for a living and this is the first time I've seen this! – Dave Doknjas Aug 31 '16 at 17:46
  • I doubt this is for backward compatibility. There were no static classes in VB6, all classes had to be COM-compatible which means having a parameterless constructor. There were and are modules, but you couldn't store a reference to a module in a variable in VB6, and you still can't in VB.NET. So this has to be a new thing. – GSerg Aug 31 '16 at 17:50
  • @GSerg: It's not enough that VB has all this legacy baggage - they have to add new quirky stuff! – Dave Doknjas Aug 31 '16 at 17:53
  • Strange this is - you can't do anything with the object once you've declared it this way... – Dave Doknjas Aug 31 '16 at 17:55

1 Answers1

7

There is actually no such thing as a Shared class in VB.NET. (Shared is the VB.NET keyword for what we know and love as static in C#). Only member variables (fields), events, functions/subs, properties, and operators can be marked Shared. (Local variables that retain their values across invocations are not marked Shared, but rather Static. The CLR doesn't natively support these and neither does C#, but the VB.NET compiler emits the additional code necessary to make it work.)

There are two workarounds to effect the same result as a static class in VB.NET:

  1. Create a Module. Practically, this is the same as a static class. Indeed, from an IL perspective, they are identical. However, modules are treated slightly differently in the VB.NET language proper. The concept of modules was inherited from legacy COM-based VB (actually, they are much older than that, going back to early versions of BASIC)

    If you're familiar with C++ or similar languages, a module is akin to a namespace, except that all of the functions defined in a module are promoted into the outer scope (enclosing namespace), via the Microsoft.VisualBasic.CompilerServices.StandardModule attribute. A module can never be treated like a class, so any declarations of or references to a module are illegal. However, you can still (and should generally prefer) to call a function defined in a module using a fully-qualified reference (e.g., MyModule.MyFunction(...)).

  2. Create a NotInheritable class (VB.NET equivalent of C#'s abstract keyword), give it an empty Private constructor, and mark all of its methods as Shared. Note that the class itself is not actually shared/static—just the individual functions that make up that class. The compiler therefore does not enforce the rule that an instance of that class cannot be declared, because from the compiler's perspective, the class is no different than any other class.

Since the .NET BCL was written in C#, it uses option 2. All classes that were marked static are simply NotInheritable classes in VB.NET with Shared member functions and private constructors. For example, the declaration of System.IO.File appears as follows to VB.NET:

Public NotInheritable Class File
    Inherits System.Object

    ' Contains Shared methods, e.g.:

    Public Shared Sub Copy(sourceFileName As String, destFileName As String)
        ' implementation
    End Sub

    ' private constructor to prevent instantiation
    Private Sub New()
    End Sub

End Class

Note that since only instantiation is prohibited (via making the constructor private), the compiler still allows you to declare empty variables of that class type. They can never be assigned a value, of course, so they will always be equal to Nothing. (TypeOf(test) Is Nothing will return True, in this case, as will test Is Nothing, and TypeName(test) will return Nothing).

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • ... and it further proves this point that if you try to instantiate `test = New System.IO.File` it will fail because `Type 'System.IO.File' has no constructors`, not because it's a static class. (So you should probably add a private constructor to the example.) – GSerg Aug 31 '16 at 18:05
  • 1
    @GSerg Working on that while you were writing the comment. Done now. :-) Been a *very* long time since I worked in VB. – Cody Gray - on strike Aug 31 '16 at 18:08
  • I've accepted your answer, but I think VB *should* be able to look at this and notice that it's a ridiculous declaration. The only thing an object of a static type can ever be is an unreferenced object. It can't be assigned anything and cannot access anything. – Dave Doknjas Aug 31 '16 at 18:43
  • 1
    I don't know how you propose that it would look at it and make that determination. I've explained from the perspective of the compiler and the language that there is nothing that looks awry about it. The C# compiler also does not prevent declaring variables of types whose constructor is private. You need them for object references, abstract classes, factories, etc. @dave – Cody Gray - on strike Aug 31 '16 at 18:48
  • @CodyGray: I think VB has access to the same information via the assembly which defines System.IO.File that C# has, yet C# finds that information and uses it. I don't buy that a language difference prevents VB from knowing the same thing about the type that C# does. – Dave Doknjas Aug 31 '16 at 18:58
  • @DaveDoknjas The very concept of "static class" does not exist in VB, so the VB compiler cannot reason about such things. Instead it converts them to something that is fully expressible in the VB grammar and behaves as closely to what is written in the IL as possible. Another example would be C# stumbling upon a parametrized property - it is supported in IL but does not exist in the C# grammar, so the C# compiler [represents it as a method](http://stackoverflow.com/questions/21190900/is-it-possible-to-make-multi-parameter-properties#comment31926889_21190975). – GSerg Aug 31 '16 at 19:04
  • @DaveDoknjas What you are saying is that compilers must issue errors based on the grammar of all other languages too - that would be silly, confusing and generally impossible. – GSerg Aug 31 '16 at 19:08
  • @GSerg: I follow your reasoning, but I think that allowing a meaningless declaration is a flaw somewhere. – Dave Doknjas Aug 31 '16 at 19:14
  • 2
    @CodyGray, Overall a good explanation +1, but `"(So MyModule.MyFunction would be illegal. You would just call MyFunction.)"` is incorrect. In fact the fully qualified version is recommended. See: [Type Promotion (Visual Basic)](https://msdn.microsoft.com/en-us/library/xz7s1h1x.aspx). On a twisted note, this Type Promotion silliness is supported in VB by the presence of the `Microsoft.VisualBasic.CompilerServices.StandardModuleAttribute` on a class, so you could make a class compiled in any .Net language inherit this _feature_ for use in VB. – TnTinMn Sep 01 '16 at 01:53
  • @TnTinMn Man, I could have sworn it was impossible to fully-qualify with a module name. I guess I'm getting confused with VB 6. As I said, it's been a long time since I wrote any code in VB. I've edited the invalid claim out of my answer! The question just piqued my interest, and I had to investigate. – Cody Gray - on strike Sep 01 '16 at 10:25
  • 1
    @CodyGray Qualifying with module name works just fine in VB6/VBA too, I use it quite a lot to not lose track of where all these functions come from. – GSerg Sep 01 '16 at 12:46