views:

40

answers:

3

Suppose we have a class like:

Public Class Question 

    Private Shared _field as Integer = CrazyIntegersRepository.GetOne()

    ' Some other useful things go here

End Class

And the method GetOne throws an exception... How can we manage that? Is a good practice to rewrite that into a static constructor?
When is the GetOne method going to be executed if we leave it there in the inline _field declaration?

+4  A: 

Note: I'm assuming VB works the same as C# here. I'd be surprised if they differed on this.

If you leave it there (and don't have a static constructor), it will depend on the version of .NET you're using. It's only guaranteed to be run "at some point before the first reference to a static field". You can even create instances and the type initializer may not run! If you have a static constructor (even an empty one), the type initializer will be run directly before the first reference to any constructor or any static member. (Basically, almost anything you actually do with it will initialize it.)

The actual observed behaviour has become lazier in .NET 4 compared with .NET 3.5, as I blogged about. Note that this is only talking about the desktop framework; I don't know what Silverlight or the Compact Framework do.

If the method can throw an exception, I'd be tempted to do it rather more lazily in the first place, in a method call, maybe caching the result appropriately. That way, the method can let the exception bubble up and the caller can try again later. That's appropriate if it's a potentially transient exception you're considering. If it's something which indicates the whole system is unusable, it's fine to let the type initializer fail.

Jon Skeet
From what I've seen, once the type initializer throws an exception, there's no way to recover. The only way to avoid this is to prevent the exception from bubbling up, either by catching it in the static constructor (when the field is initialized there) or by catching it in the helper method that the field is initialized to. Am I missing anything here?
Steven Sudit
@Steven: As I say, my approach would usually be to take it out of type initialization to start with - only calculate it when you *need* it, and then let the exception bubble up to the caller.
Jon Skeet
Fair enough. I'm guessing this would be an ideal spot for `Lazy<T>`, then.
Steven Sudit
@Steven: I don't know offhand whether `Lazy<T>` allows for retries, but if so, yes :)
Jon Skeet
No space to toss out code here, but I believe it's possible. Thanks again.
Steven Sudit
A: 

Regarding your question around managing the possible exceptions, go with your gut on that one. Any code that can create an exception should be put into a method. In this case, the constructor would be the best place. So perhaps something like:

Public Sub New()
    Try
        _field as Integer = CrazyIntegersRepository.GetOne()
    Catch ex As Exception
        'log it / deal with it as you will
    End Try
End Sub
p.campbell
A: 

If you go by p.campbell's suggestion, you'll have to declare at least one instance of the class in order to have the _field variable initialised. I assume, from the Shared keyword in the declaration of the variable, that you want it to be accessible from all instances of the class whether or not they have been specifically initialised.

In order to achieve that functionality, you'll have to modify your class as follows:

Public Class Question 

    Private Shared _field as Integer

    Shared Sub New()
        _field = CrazyIntergersRepository.GetOne()
    End Sub
    ' Some other useful things go here

End Class


Using this method, the _field variable will be initialised the first time the class is used because the default constructor is declared as Shared. You could optionally wrap the method in a Try...Catch block in order to trap exceptions that might occur.

Alex Essilfie