29

After debugging a particularly tricky issue in VB.NET involving the order in which instance variables are initialized, I discovered that there is a breaking discrepancy between the behavior that I expected from C# and the actual behavior in VB.NET.

Nota bene: This question concerns a slight discrepancy in the behaviors of VB.NET and C#. If you're a language bigot that is unable to provide an answer other than "that's why you should use C#, noob", there is nothing for you to see here; kindly move along.

Specifically, I expected the behavior outlined by the C# Language Specification (emphasis added):

When an instance constructor has no constructor initializer, or it has a constructor initializer of the form base(...), that constructor implicitly performs the initializations specified by the variable-initializers of the instance fields declared in its class. This corresponds to a sequence of assignments that are executed immediately upon entry to the constructor and before the implicit invocation of the direct base class constructor. The variable initializers are executed in the textual order in which they appear in the class declaration.

Contrast that with the portion of the VB.NET Language Specification concerning Instance Constructors, which says (emphasis added):

When a constructor's first statement is of the form MyBase.New(...), the constructor implicitly performs the initializations specified by the variable initializers of the instance variables declared in the type. This corresponds to a sequence of assignments that are executed immediately after invoking the direct base type constructor. Such ordering ensures that all base instance variables are initialized by their variable initializers before any statements that have access to the instance are executed.

The discrepancy here is immediately obvious. C# initializes class-level variables before calling the base constructor. VB.NET does exactly the reverse, apparently preferring to call the base constructor before setting the values of instance fields.

If you want to see some code, this related question provides a more concrete example of the divergent behavior. Unfortunately, it does not provide any hints as to how one might coerce VB.NET into following the model established by C#.

I'm less interested in why the designers of the two languages chose such divergent approaches than I am in possible workarounds for the problem. Ultimately, my question is as follows: Is there any way that I can write or structure my code in VB.NET to force instance variables to be initialized before the base type's constructor is called, as is the standard behavior in C#?

Community
  • 1
  • 1
Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
  • 1
    I don't believe it's possible to change this - and the VB compiler will fight against any attempts to do so. If you're relying on such behaviour, it's usually an indication of problems elsewhere in your code. There has been advice against calling virtual members within constructors for a long time (as in the sample code in the other question) – Damien_The_Unbeliever Jan 05 '11 at 09:45
  • @Damien: You're right about it being bad practice to call virtual members within constructors. Unfortunately, that decision wasn't mine to make. I'm subclassing a type provided by the Framework while also overriding one of its virtual methods. I am pretty sure that the problems lie outside of my code, and the only thing I can think of to fix this screams "ugly hack" to me. – Cody Gray - on strike Jan 05 '11 at 09:49
  • see also http://stackoverflow.com/q/12585555/185593 – serhio Sep 25 '12 at 15:09

2 Answers2

8

If you have virtual members that are going to be invoked during construction (against best advice, but we've already agreed on that), then you need to move your initialization into a separate method, that can protect itself against multiple calls (i.e. if init has already happened, return immediately). That method will then be invoked by the virtual members and your constructor, before they rely on initialization having occurred.

It's a bit messy, and may represent a minor performance penalty, but there's little else you can do in VB.

Damien_The_Unbeliever
  • 234,701
  • 27
  • 340
  • 448
2

Any well-written class must either ensure any virtual members which could possibly be invoked on a partially-constructed instance will behave sensibly. C# takes the philosophy that a class instance whose field initializers have run will be in a sufficiently-sensible state to allow virtual methods to be used. VB.net takes the philosophy that allowing field initializers to make use of a partially-constructed object (and--with a little work--any parameters passed to the constructor) is more useful than a guarantee that field initializers will run before any virtual methods are called.

IMHO, the right approach from a language-design standpoint would have been to provide a convenient means of indicating that specified field initializers should be run "early" or "late", since there are times each can be useful (though I prefer the "late" style, since it allows constructor parameters to be made available to field initializers). For example:

Class ParamPasserBase(Of T) ' Generic class for passing one constructor parameter
  Protected ConstructorParam1 As T
  Sub New(Param As T)
    ConstructorParam1 = Param
  End SUb
End Class
Class MyThing
  Inherits ParamPasserBase(Of Integer)
  Dim MyArray(ConstructorParam1-1) As String
  Sub New(ArraySize As Integer)
    MyBase.New(ArraySize)
  End Sub
  ...
End Class

In C#, there's no nice way to have field declarations or initializers make use of arguments passed to a constructor. In vb, it can be done reasonably cleanly as illustrated above. Note that in vb, it's also possible to use a by-reference constructor parameter to smuggle out a copy of the object under construction before any field initializers are run; if a class's Dispose routine is properly written to deal with partially-constructed objects, an object which throws in its constructor can be properly cleaned up.

supercat
  • 77,689
  • 9
  • 166
  • 211
  • 1
    I'm not sure I follow what you're trying to say here. I don't have the ability to redesign either language, and I'm not particularly arguing over the relative merit of the choices. Furthermore, where does `RIAA()` come from in your code sample? You say that one can do this now in VB.NET. I'm not familiar with the syntax. Is `RIAA()` a custom wrapper class that you've written? And how is that different from a `using` statement? Beyond that, what's the distinction between "older" VB.NET and "newer" VB.NET? One is type safe and the other isn't? What does destruction have to do with the question? – Cody Gray - on strike Feb 12 '11 at 04:22
  • @Cody Gray: RIAA is a class method which registers an IDisposable for deterministic automatic cleanup (in the style of C++/RIAA) and then returns it. In vb.net, the field initializers run after the list of disposables is created, so the RIAA method can add them to that list. VB.Net also allows an object under construction to be smuggled out of the constructor, so the constructor can be wrapped in a factory method which will clean up any registered IDisposable objects if the constructor throws. – supercat Feb 12 '11 at 05:48
  • Can you give me a link to where this RIAA method is discussed? I can't find the documentation online. I'm also not sure what disposing items has to do with initializing instance variables before the base class constructor is called. My question is not even about objects, much less those that implement `IDisposable`. The test case was actually an `Integer` or `Boolean` type, both of which are value types and do not need to disposed. RIAA is as useless here as it is irrelevant. – Cody Gray - on strike Feb 12 '11 at 05:51
  • @Cody Gray: Sorry I was unclear. RIAA is a method in some of my classes which achieves the stated functionality when used in conjunction with other code. My point was that vb.net allows one to declare, initialize, and clean up an field in a single line of code, rather than having to have one area of the source code for object declarations, another area for initialization, and a third area for cleanup. Keeping all three aspects of a field together seems cleaner and less error-prone than having them separate. – supercat Feb 12 '11 at 05:55
  • 1
    I still don't get it. First of all, how is a method you've implemented in your own programs going to help me here? Second, the rationale behind implementing such a method escapes me. You can wrap the object in a `using` statement, which has the additional benefit of limiting its scope. I don't have any problems with the syntax of the language; my issue is with the behavior described in the spec. And third, what does this have to do with value types that don't implement `IDisposable`? The question I linked to with some sample code isn't calling any constructors. RIAA won't do any good there. – Cody Gray - on strike Feb 12 '11 at 05:59
  • @Cody Gray: VB.net runs field initializers after the base constructor. That is simply what it does. My point was to suggest cases where one might be able to take advantage of such behavior, since it is what it is. The "Using" statement works great for local variables, but there's no equivalent for fields. The ability to mimic C++/RIAA automatic cleanup is nice, but probably not the most generally-useful scenario enabled by late allocation. I'll add an illustration of a more generally-useful technique permitted by late allocation. – supercat Feb 14 '11 at 16:14
  • @Cody Gray: As for 'using' statements, they only work with local variables. There is no equivalent in C# or vb.net for fields. Further, a "using" statement will only clean up an object whose constructor manages to run completely without throwing. A constructor which throws an exception is supposed to handle the cleanup for the object being constructed, but there are many cases where that's difficult. If Dispose routines properly handle partially-created objects and ignore redundant calls, cleanup outside the constructor may be easier. – supercat Feb 14 '11 at 16:41
  • @supercat: I'm sorry. My confusion arose out of the fact that you posted an answer. I assumed you were answering the question ("Can VB.NET be forced to initialize instance variables before invoking the base type constructor?") I see now that you were just posing some ways that I "might be able to take advantage of such behavior." That's not particularly helpful to me, though. I've never missed RIAA in either C# or VB.NET, and don't really feel the need to simulate it. I do have one particular case, though, that I'd like to get VB.NET to work with the equivalent code in C#, hence the question. – Cody Gray - on strike Feb 15 '11 at 04:42
  • @Cody Gray: Because questions and answers are often read by many people other than the original question-askers, I try to offer information that may be helpful to other readers. Sometimes I go perhaps too far off-topic, though I sometimes get up-votes for my related observations, so I think people at least sometimes find them useful. What do you think about my revised answer? – supercat Feb 15 '11 at 15:38
  • it's a clever approach but with limited use due to the lack of multiple inheritance. if you already need to inherit from some other class the approach cannot be used. – ekkis May 07 '13 at 22:59
  • @ekkis: If one wants to use a constructor parameter within initializer expressions, but inherit from a base type that does not, simply define a `MyThingParentParamPasserBase` that inherits from `MyThingParent` and which includes the appropriate protected field(s) and an internal constructor but nothing else, and then derive your real type from that. – supercat May 07 '13 at 23:08