Public Shared Readonly Member optimized out in VB.Net? - vb.net

I have this weird behavior in VB.Net I am stuck with. At the moment I am not able to determine whether I am missing some concept or if it is a bug.
I have a base class:
Public MustInherit Class BaseType(Of T As BaseType(Of T)) :
Implements IEquatable(Of BaseType(Of T))
Protected Sub New(key As Integer, value As List(Of String))
LogManager.GetLogger("INFO").Info("Strings: " & vbTab & value.Count())
If key <> -1 Then
enumValues.Add(CType(Me, T))
End If
End Sub
Protected Shared Function TypeFromStringBase(name As String) As T
For Each b As T In enumValues
LogManager.GetLogger("DEBUG").Info(b.Names(0))
If b.Names.Contains(name) Then
Return b
End If
Next
Return TypeFromStringBase("Keine")
End Function
End Class
And a Class that inherits it:
Public Class AnschlussType : Inherits BaseType(Of AnschlussType) :
Implements IEquatable(Of AnschlussType)
Public Shared ReadOnly Rund As New AnschlussType(1, {"Rund", "1"})
Public Shared ReadOnly Flach As New AnschlussType(2, {"Flach", "2"})
Public Shared ReadOnly Gewinde As New AnschlussType(3, {"Gewinde", "3"})
Public Shared ReadOnly Kein As New AnschlussType(4, {"Kein", "None", "4"})
Private Sub New(key As Integer, names As String())
MyBase.New(key, names.ToList())
End Sub
Public Shared Function TypeFromString(name As String) As AnschlussType
Return TypeFromStringBase(name)
End Function
End Class
Here is the weird part I don't get. The first time you call AnschlussType.TypeFromString("Some String"), VB should create all the Public Shared ReadOnly members. This results in four calls to BaseType.New. Each of those calls then adds its own type to the enumValues List.
After all those initializations, finally, the AnschlussType.TypeFromString call will be executed. There it calls TypeFromStringBase which iterates over the enumValues List we filled right before.
This all works fine in the DEBUG mode.
Here is the weird part I don't get. Now I tried RELEASE mode. The enumValues.Count would always stay 0. I assumed this because the logger does not print anything, which means it doesn't iterate, which means it is zero. So I investigated a little more and put a logging statement into BaseType.New. And this does NOT log at all. This leads me to the conclusion that New is not executed at all.
Let me emphasize that this all works great in DEBUG mode and with other subtypes that have Public Shared ReadOnly members in the same matter.
But this does not work in RELEASE mode.
Does anyone have a hint for me what I could be missing?

If a static constructor (Section 10.11) exists in the class, execution of the static field initializers occurs immediately prior to executing that static constructor. Otherwise, the static field initializers are executed at an implementation-dependent time prior to the first use of a static field of that class.
Assuming VB works like C#, your shared (i.e. static) fields aren't being initialized because you haven't used them.
Try creating a shared constructor.

Related

Custom typed, non-generic, read-only, fixed-sized collection class in VB.Net?

I'm looking for suggestions on how to create a custom collection class in VB.Net, to contain instances of a custom object class. There is so much information on the topic that I'm not sure which direction to go in, and custom collections are new to me.
The collection needs are as follows:
collection should be read-only, it and its objects cannot be modified.
collection will always be a fixed size.
because of above two items, add, remove, count, clear, etc aren't needed.
will create & manage all object instances itself at instantiation.
should have a default property like "Item". (?)
should be enumerable, so For-Each can be used on it.
Here is a simplified outline of what I've pieced together so far:
Class Bay
Private ID As Integer
Private p_String As String
Private p_Aisle As Integer
...etc...
...getters, setters, & subs...
End Class
Class Bays
Inherits ...something...
Implements ....somethingelse... (?)
Public ReadOnly MyCollection(5094) as SomeCollectionType (Of Bay)
Private LastUpdate As Date
Private SystemStatus as Integer
Public Sub New()
...instantiate all objects in collection...
End Sub
...properties...
End Class
This is the sort of thing you can do:
Public Class Thing
'...
End Class
Public Class ReadOnlyThingCollection
Inherits ReadOnlyCollection(Of Thing)
Public Sub New()
MyBase.New(GetItems())
End Sub
Private Shared Function GetItems() As IList(Of Thing)
'Generate items as appropriate.
Return {New Thing, New Thing, New Thing}
End Function
End Class

Order of initialisation

I'm playing with the following:
Public MustInherit Class TempTable
Public Sub New()
For Each f As FieldInfo In Me.GetType().GetFields
Dim l As TypedLeaf = CType(f.GetValue(Me), TypedLeaf)
Console.WriteLine(l.Name)
Next
End Sub
End Class
Public Class JMTempTable
Inherits TempTable
Public KeyIndex As New TypedLeaf(Me, "KeyIndex", OQL.Type.AUTONUMBER)
Public Debit As New TypedLeaf(Me, "Debit", OQL.Type.DECIMAL(16, 2))
Public Sub New()
MyBase.New()
End Sub
End Class
but getting Nothing for the values retrieved. The reason seems to be that the derived class' fields do not get initialised until after the base class' constructor is called... to further complicate matters, if I were to do:
Public Class JMTempTable
Inherits TempTable
Public KeyIndex As TypedLeaf
Public Debit As TypedLeaf
Public Sub New()
KeyIndex = New TypedLeaf(Me, "KeyIndex", OQL.Type.AUTONUMBER)
Debit = New TypedLeaf(Me, "Debit", OQL.Type.DECIMAL(16, 2))
MyBase.New()
End Sub
End Class
The compiler will complain that the base class constructor must be called in the first line of the derived class' constructor...
Is there a way I can delay the base class constructor from running until after the derived class' fields have been initialised?
Here's one way (perhaps the way) to do it:
Public MustInherit Class TempTable
Public Sub New()
Initialize()
For Each f As FieldInfo In Me.GetType().GetFields
Dim l As TypedLeaf = CType(f.GetValue(Me), TypedLeaf)
Console.WriteLine(l.Name)
Next
End Sub
Protected MustOverride Sub Initialize()
End Class
Public Class JMTempTable
Inherits TempTable
Public KeyIndex As TypedLeaf()
Public Debit As TypedLeaf()
Public Sub New() ' Optional block. You don't have to explicitly define a default constructor.
MyBase.New()
End Sub
Protected Overrides Sub Initialize()
KeyIndex = New TypedLeaf(Me, "KeyIndex", OQL.Type.AUTONUMBER)
Debit = New TypedLeaf(Me, "Debit", OQL.Type.DECIMAL(16, 2))
End Sub
End Class
The abstract Initialize() method forces inheritors to have a method called Initialize(). This method is implicitly called when you call MyBase.New(). That means you can now move your initialization logic out of the constructor and into the Initialize() method to get the effect you're looking for.
This is well known behavior in managed languages in general. Surprisingly I can't find it explicitly mentioned in the VB.NET Language Specification so I'll have to wing it by explaining it myself.
The CLI has direct support for field initializers but they are not strong enough to support your fields. They can only store simple data, think value types. Initializing a reference type, like your TypedLeaf class requires executing code. And code cannot be stored in a field initializer, it can only appear inside of a method.
So the VB.NET compiler works around that restriction by moving your field initialization expression to the next logical place, the class constructor. This is entirely automatic, it actually rewrites your constructor, in case you provide one yourself, injecting the new operator calls as needed.
Now there's a choice, it could move those calls before or after the base class constructor call. You already know the choice that was made, it happens after. With the justification that a field initializer should not be able to observe members of the base class that are not yet initialized. Your attempt at a workaround is actually pretty heroic compiler writing skills, it actually checks that the base constructor is called first.
Unfortunately you found a case where you are actually happier if it happened before the base constructor call. That's justifiable but unfortunately not permitted, the language designers put their foot down and declared "we only support one way to do this". Fair call, such basics need to be predictable.
The workaround is simple. Just put a Protected method in your base class, say "Initialize", and move the code you now have in the constructor to that method. In the derived class constructor just call that method. The constructor rewriting ensures that the base constructor call is first and the field initializer code is second, making the method call third. Minus 33.3 points for having to remember to make that call so add the code to throw an InvalidOperationException when you see Nothing.

Create a shared IEqualityComparer

I'm doing some LINQ which requires a custom comparer, so I created a new class implementing IEqualityComparer. However, when I use it, I have to create an instance of it each time.
Dim oldListOnly = oldList.Except(newList, New MyEqualityComparer)
Dim newListOnly = newList.Except(oldList, New MyEqualityComparer)
I may be misunderstanding how .NET works, but it seems wasteful to create a new comparer each time. I really just want one instance (the equivalent of static in C++/C#).
So I tried creating a "static" class, which in vb.net is a module. But got an 'Implements' not valid in Modules error.
I then tried making the Equals and GetHashCode function shared methods on my class, but got this error: Methods that implement interface members cannot be declared 'Shared'.
Any ideas how to accomplish my goal here? Or am I simply misunderstanding what's going behind the scenes?
Your understanding is correct, although the waste is unlikely to be noticeable. For your situation, you could use the singleton pattern, which usually goes something like this:
Public Class MyEqualityComparer
Implements IEqualityComparer(Of whatever)
Private Sub New()
'no outsider creation
End Sub
Private Shared ReadOnly _instance As New MyEqualityComparer()
Public Shared ReadOnly Property Instance As MyEqualityComparer
Get
Return _instance
End Get
End Property
'other code
End Class
Why not simply do
Dim comparer = New MyEqualityComparer
Dim oldListOnly = oldList.Except(newList, comparer )
Dim newListOnly = newList.Except(oldList, comparer )
There needs to be an instance of a concrete type that implements IEqualityComparer. What you can do with a module, however, is define a public instance which is initialized to "New EqualityComparer". You can then pass that default instance to the Except method.
Something like:
Public Module MyComparer
Public acmeComparer As acmeCompareType
Public Class acmeCompareType
Implements IEqualityComparer(Of System.Drawing.Point)
Public Function Equals1(x As System.Drawing.Point, y As System.Drawing.Point) As Boolean Implements System.Collections.Generic.IEqualityComparer(Of System.Drawing.Point).Equals
Return Math.Abs(x.X) = Math.Abs(y.X) AndAlso Math.Abs(x.Y) = Math.Abs(y.Y)
End Function
Public Function GetHashCode1(obj As System.Drawing.Point) As Integer Implements System.Collections.Generic.IEqualityComparer(Of System.Drawing.Point).GetHashCode
' Note that obj is a struct passed by value, so we can safely modify it here
' (without affecting the caller's instance)
obj.X = Math.Abs(obj.X)
obj.Y = Math.Abs(obj.Y)
Return obj.GetHashCode
End Function
End Class
End Module

Private Variable Instantiation: When Defined or Within Constructor?

I don't know if this has been asked before, but we're having a discussion about it today at my job. Should private variables (that are shared/static) be instantiated when they are dimensioned/defined, or is it a better practice to do this inside of a constructor?
For example, this seems perfectly fine to me...
Public Class IpCam
Private Const HOST As String = "http://test.com/url/example"
Private Shared _Example As New OurClass(HOST)
Public Shared ReadOnly Property Example() As OurClass
Get
Return _Example
End Get
End Property
End Class
But others are telling me that it should be done like this...
Public Class IpCam
Private Const HOST As String = "http://test.com/url/example"
Private Shared _Example As OurClass
Public Sub New()
_Example = New OurClass(HOST)
End Sub
Public Shared ReadOnly Property Example() As OurClass
Get
Return _Example
End Get
End Property
End Class
What is the difference? Is there a common consensus as to which one to use?
It's really a matter of preference. I think what's more important is consistency: if you instantiate a few variables inline, and others in a constructor, it can get harder to maintain, as it's unclear what is instantiated where.
A good idea is to keep variable declarations just above your constructor (so you don't have to jump around to find all the variable instantiations), and instantiate everything inline. For those few objects which require more complex initialization code, you can use a constructor.
I wonder if your second example is a hangover from the old VB6 days when good practise meant generally avoiding As New declarations because it wasn't optimal (auto-instantiation meant a run-time check each time) and you could never reliably test the instance for Is Nothing etc.
Member variables are initialized before the constructor; otherwise everything else is equivalent, so it's entirely up to you. I would go for what's more legible/maintainable/leads to fewer errors.
One benefit to initializing the variables inline is that you do not have to remember to put the initialization in each constructor or make sure each other constructor calls the one with the initialization. Take this code for example:
Public Class Person
Public Sub New()
_name = "asdlfkj"
End Sub
Public Sub New(ByVal age As Integer)
_age = age
End Sub
Private _name As String
Public ReadOnly Property Name As String
Get
Return _name
End Get
End Property
Private _age As Integer = 17
Public ReadOnly Property Age As Integer
Get
Return _age
End Get
End Property
End Class
Calling the first constructor will put in a default name, but calling the second will not.
Conversely, if you ever need to initialize the variable different ways for different constructors, I would definitely say to initialize in the constructor.

How to setup return value for a readonly property using RhinoMocks in VB.NET?

I'm using RhinoMock in VB.NET and I need to set the return value for a readonly list.
Here's what I want to do (but doesn't work):
dim s = Rhino.Mocks.MockRepository.GenerateStub(of IUserDto)()
s.Id = guid.NewGuid
s.Name = "Stubbed name"
s.Posts = new List(of IPost)
It fails on the compile because Posts is a readonly property.
Then I tried a lambda expression, which works fine for Function calls, but not so much for Properties. This fails to compile.
s.Stub(Function(x As IUserDto) x.Posts).Return(New List(Of IPost))
Next (failing) attempt was to use SetupResults, but this failed stating that it cannot be used in Playback mode.
Rhino.Mocks.SetupResult.For(s.Posts).Return(New List(Of IPost))
Which brings me back to my question:
How do I setup a return value for a readonly property using RhinoMocks in VB.NET?
Is IUserDto an interface? If it is then it should just work. If it isn't, then the problem could be that the readonly property in question is not overridable. RhinoMocks can only mock properties/methods which are defined in an interface or can be overridden.
Here is my (clumsy) attempt at proving that the lambda syntax should work:
Imports Rhino.Mocks
Public Class Class1
Public Sub Test()
Dim s = MockRepository.GenerateMock(Of IClass)()
Dim newList As New List(Of Integer)
newList.Add(10)
s.Stub(Function(x As IClass) x.Field).Return(newList)
MsgBox(s.Field(0))
End Sub
End Class
Public Class AnotherClass
Implements IClass
Public ReadOnly Property Field() As List(Of Integer) Implements IClass.Field
Get
Return New List(Of Integer)
End Get
End Property
End Class
Public Interface IClass
ReadOnly Property Field() As List(Of Integer)
End Interface
i.e. I would get a message box with the number 10 displayed on it (I didn't bother to try hooking up a unit-testing framework with this but that shouldn't make a difference) when Class1.Test is invoked.
Hope that helps (it was an interesting exercise trying to work with RhinoMocks in VB.NET in anycase).