Initializing a collection auto-property inline: behaviour seems different between C# and VB

Viewed 199

Context: I am converting a program from C# to VB. The program runs with Entity Framework (latest, but not EFCore).

In a C# class, I have an auto-property for a collection, initialised thus:

public virtual ICollection<Student> Students {get; set;} = new List<Student>();

This runs fine with EF.

Converted to VB this becomes:

Public Overridable Property Students As ICollection(Of Student) = New List(Of Student)()

Strangely, the latter seems not work with Entity Framework, yielding a run-time error stating

The property 'Students' ... cannot be set because the collection is already set to an EntityCollection.

I can fix this error by reverting to the older long-winded way to initialise a collection (which I used to have to do in C# also before it had initialization of auto-properties):

Private _students As ICollection(Of Student) = New List(Of Student)

Public Overridable Property Students As ICollection(Of Student)
  Get
    Return _students
  End Get
  Set(value as ICollection(Of Student))
    _students = value
  End Set
End Property

I had thought that the above long-winded code and the single line that replaced it were functionally equivalent. Can anyone explain to me why they are different AND why/how this is different to the C# equivalent (where the long-winded and one-line equivalents appear to have the same behaviour).

2 Answers

Some further investigation does not fully answer my question, but does at least confirm that there is a difference between how the C# and VB versions of the initialized collection auto-property work.

In the C# version, if I add a zero param constructor (empty implementation) and put a breakpoint on it, then when retrieving an object (via EF), by the time the constructor is hit, the auto-property has already been initialized with a new List. (This then gets replaced, presumably after the constructor exits, with the EntityCollection from EF).

In the VB version, if I add a zero param constructor (empty implementation) and put a breakpoint on it, then when retrieving an object from EF, by the time the constructor is hit, the auto-property is still Nothing (null).

So I'm guessing that in VB, immediately the constructor exits, EF gets in and puts its EntityCollection in the property before the property initializer is then called. This scenario would be consistent both with the breakpoint observation and the error message.

I find it very surprising that C# and VB should differ on something as basic as the construction sequence.

(N.B. It probably doesn't make a difference in many circumstances, but I'm guessing it makes a difference in EF because the latter is doing dynamic proxying.)

I think the root of the problem is that VB initializes fields after calling the base class constructor while C# initializes fields before calling the base class constructor. https://anthonydgreen.net/2019/02/12/exhausting-list-of-differences-between-vb-net-c/#:~:text=16.-,VB%20initializes%20fields%20AFTER%20the%20base%20constructor%20call%20whereas%20C%23,BEFORE%20the%20base%20constructor%20call

This is a hard problem to account for when converting code - you just have to be aware of it. It's especially difficult converting from C# to VB, because as far as I can see, there's no way to force field initialization before the base class constructor call since if "MyBase.New" exists, then it must be the first statement in the body of a constructor in VB.

Related