Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interesting Property Behavior

Tags:

c#

.net

vb.net

Anyone have any ideas why this doesn't work (C# or VB.NET or other .NET language doesn't matter). This is a very simplified example of my problem (sorry for VB.NET):

    Private itsCustomTextFormatter As String
    Public Property CustomTextFormatter As String
        Get
            If itsCustomTextFormatter Is Nothing Then CustomTextFormatter = Nothing  'thinking this should go into the setter - strangely it does not'
            Return itsCustomTextFormatter
        End Get
        Set(ByVal value As String)
            If value Is Nothing Then
                value = "Something"
            End If
            itsCustomTextFormatter = value
        End Set
    End Property

If you do:

Dim myObj as new MyClass
Console.WriteLine(myObj.CustomTextFormatter)

You will be surprised at the result. It will print "Nothing". Anyone have any idea why it doesn't print "Something"

Here's a Unit Test per suggestion:

Imports NUnit.Framework

<TestFixture()> _
Public Class Test
   Private itsCustomTextFormatter As String
    Public Property CustomTextFormatter As String
        Get
            If itsCustomTextFormatter Is Nothing Then CustomTextFormatter = Nothing 'thinking this should go into the setter - strangely it does not' 
            Return itsCustomTextFormatter
        End Get
        Set(ByVal value As String)
            If value Is Nothing Then
                value = "Something"
            End If
            itsCustomTextFormatter = value
        End Set
    End Property

    <Test()>
    Public Sub Test2()
        Assert.AreEqual("Something", CustomTextFormatter)
    End Sub
End Class

This returns:

Test2 : Failed  
  Expected: "Something"
  But was:  null

at NUnit.Framework.Assert.That(Object actual, IResolveConstraint expression, String message, Object[] args)
at NUnit.Framework.Assert.AreEqual(Object expected, Object actual)
like image 715
Denis Avatar asked Apr 25 '12 15:04

Denis


2 Answers

Your comment:

'thinking this should go into the setter - strangely it does not'    

calls out what your error is. In Visual Basic there are two ways to return something from a function:

Function GetSomeValue() As String
    Return "Hello"
End Function

or

Function GetSomeValue() As String
    GetSomeValue = "Hello"
End Function

Mixing these two styles is perfectly legal, but confusing and a bad practice:

Function GetSomeValue() As String
    GetSomeValue = "Hello" ' I should return Hello... '
    Return "GoodBye"       ' ...or perhaps not. '
End Function

As you can see, you are mixing the two styles in the getter. Setting that variable does not call the setter; it informs the runtime that when the getter returns, this is the value that it should return. You then override that value with your Return statement.

If it hurts when you do that then don't do that.

like image 135
Eric Lippert Avatar answered Oct 02 '22 10:10

Eric Lippert


Works fine for me in a unit test; albeit in C# (see below).

(after some playing)

Ah got it! It's because you're calling CustomTextFormatter = Nothing which, in the scope of the Getter is actually just setting the return value of the enclosing accessor method - it's not actually firing the setter (if you put a breakpoint in your setter you will see and debug it you'll see it steps right over it).

Basically you really shouldn't be doing this kind of pattern anyway; it's not the way to return a default property value. This would be better (or to use whatever the equivalent to the C# ?? operator is):

Public Property CustomTextFormatter As String
    Get
        If itsCustomTextFormatter Is Nothing Then
            Return "Something"
        End If
        Return itsCustomTextFormatter

    End Get
    Set(ByVal value As String)
        itsCustomTextFormatter = value
    End Set
End Property

Original C# Test

    private string _foo;
    private string foo
    {
        get
        {
            if (_foo == null)
                foo = null;
            return _foo;
        }
        set
        {
            if (value == null)
                value = "Something";
            _foo = value;
        }
    }

    [TestMethod]
    public void Test()
    {
        Assert.AreEqual("Something", foo);
    }
like image 33
Andras Zoltan Avatar answered Oct 02 '22 10:10

Andras Zoltan