0

Title says it all...

Please read this full compiling sample code :

Imports System.Runtime.CompilerServices
Imports System.ComponentModel
Imports System.Drawing

Public Class PropertyChangedNotifier
    Implements INotifyPropertyChanged

    Protected Sub SetProperty(Of T)(ByRef _backingStoreField As T, ByVal new_value As T, <CallerMemberName> Optional ByVal propertyname As String = "")
        ' PropertyChangedNotifier allow me to add functionality here like 
        ' PropertyChanging with old and new value
        ' Locked current object is property IsReadOnly is true etc ...
        ' Save Old Original values
        ' etc

        ' Let's keep it simple for example
        _backingStoreField = new_value
        RaiseEvent PropertyChanged(Me, New PropertyChangedEventArgs(propertyname))
    End Sub

    Public Event PropertyChanged(sender As Object, e As PropertyChangedEventArgs) Implements INotifyPropertyChanged.PropertyChanged
End Class

Public Class VehicleEngine
    Public Property OilWarningAlert As Boolean
    Public Event OilWarningAlertChanged As EventHandler
    ' [...]
End Class

Public Interface IBulbReadOnly
    ReadOnly Property State As Boolean
End Interface

' just a light bulb on car dashboard
Public Class Bulb
    Implements IBulbReadOnly

    Public Property State As Boolean 'true => On, false => Off
    Public Sub New(color As Color)
    End Sub

    Public ReadOnly Property StateAsReadOnly As Boolean Implements IBulbReadOnly.State
        Get
            Return State
        End Get
    End Property
End Class

Public Class Car
    ' this base class has methods to handle changed & chagnging event, dirty state etc
    ' It implements INotifyPropertyChanged
    Inherits PropertyChangedNotifier

    Public Property Engine() As VehicleEngine
        Get
            Return _Engine
        End Get
        Set(ByVal value As VehicleEngine)
            SetProperty(_Engine, value)
            ' The line above compile fine but actually the generated code is 
            ' Dim tempEngine = _Engine
            ' SetProperty(tempEngine, value)
            ' _Engine = tempEngine
            ' So when SetProperty raise event the value is not changed in current car instance
        End Set
    End Property
    Private WithEvents _Engine As VehicleEngine

    Public ReadOnly Property OilWarningBulb As IBulbReadOnly
        Get
            Return _OilWarningBulb
        End Get
    End Property
    Private _OilWarningBulb As Bulb = New Bulb(Color.Red) ' display warning to driver

    Private Sub Engine_OilWarningChanged(ByVal sender As Object, ByVal e As EventArgs) Handles _Engine.OilWarningAlertChanged
        'When we change engine, we synchronize all other Car composites objects
        _OilWarningBulb.State = Engine.OilWarningAlert
    End Sub

    Private Sub OnPropertyChanged(ByVal sender As Object, ByVal e As PropertyChangedEventArgs) Handles Me.PropertyChanged
        If (e.PropertyName = "Engine") Then
            Engine_OilWarningChanged() ' Synchronize the new engine with the car objects
        End If
    End Sub
    ' [...]
End Class


Module Test
    Sub Main()

        Dim car As Car = New Car()

        Dim bad_engine = New VehicleEngine()
        bad_engine.OilWarningAlert = True

        car.Engine = bad_engine
    End Sub
End Module

At first sight, this code seems fine for non expert VB.net developpers But it actually throws a NullReferenceException when settings car.Engine !

I do understand why this happens. This is because the line :

SetProperty(_Engine, value)

is actually handled by vb compiler as this block of code :

Dim tempEngine = _Engine
SetProperty(tempEngine, value)
_Engine = tempEngine

So when SetProperty raise event the current car object has still the old Engine ... (which is null/Nothing)

I dont like non explicit code So i would like to prevent compiler to generate temporary field like that... And make it consider field declared with withevents ineligible to be used as byref argument in a method call. This is, in my opinion, the behavior the compiler should have to have.

Do you know any way to do that ?

Mickaël L
  • 19
  • 3
  • possible duplicate of [What is a NullReferenceException and how do I fix it?](http://stackoverflow.com/questions/4660142/what-is-a-nullreferenceexception-and-how-do-i-fix-it) your car has no engine. never is an instance of VehicleEngine created – Ňɏssa Pøngjǣrdenlarp Jul 08 '14 at 13:15
  • 1
    The vehicle engine is created (see near the end of the code snippet), but the problem is an old VB issue - WithEvents variables are implemented as properties by the VB compiler and VB makes a copy when you pass properties ByRef. This link is for VB6, but it still applies: http://stackoverflow.com/questions/17468597/why-does-byref-not-work-in-conjunction-with-withevents – Dave Doknjas Jul 08 '14 at 14:07
  • It is a CLR restriction, it doesn't support passing accessors like Event or Property by reference. The C# compiler declares an error, the VB.NET compiler works around it. Do note that you are writing buggy code, the Car.Engine_OilWarningChanged event handler does not have the appropriate signature for an EventHandler event handler. And worse, it *assumes* that the event was raised because the warning should be turned on. Which is not the case, you raise the event just because the Engine got reassigned. The code is too convoluted, that causes bugs like this. – Hans Passant Jul 08 '14 at 14:19
  • @DaveDoknjas : I am aware about "withevents" being a syntaxic sugar keyword that creates a property for managing "handles ...." on class methods. The issue is that we see a field in code but instead we get a property... not enough wysiwyg / explicit in my opinion. In my opinion again, because compiler takes the responsability to transform it as a property, it should take the responsability to warns developper when he thinks it's a field and write code that relies on the fact it is a field (ex : using byref). – Mickaël L Jul 21 '14 at 14:26
  • @HansPassant Thanks, I corrected about events signature. Regarding the code, I removed some checks and tests like : raising event only if Engine changed, check if Engine is null before using it etc. – Mickaël L Jul 21 '14 at 15:01

0 Answers0