Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support null-conditional operator for [Add|Remove]Handler statements. #303

Closed
ericmutta opened this issue May 23, 2018 · 5 comments
Closed
Labels
LDM Reviewed: No plans LDM has reviewed and this feature is unlikely to move forward in the foreseeable future

Comments

@ericmutta
Copy link

ericmutta commented May 23, 2018

I just tried typing the following code:

      AddHandler btnEditPost?.Clicked, '<---- notice the use of ?. operator.
        Sub(ArgSender)
          Me.OnSwitchToModel(NameOf(CCreatePostVM), ArgPost)
        End Sub

...with the expectation that it would be equivalent to this:

      If btnEditPost IsNot Nothing Then
        AddHandler btnEditPost.Clicked,
          Sub(ArgSender)
            Me.OnSwitchToModel(NameOf(CCreatePostVM), ArgPost)
          End Sub
      End If

...but the compiler doesn't like it and reports:

vs_addhandler_nullconditional

The use case is as follows: in multi-user GUI apps, you typically have some UI controls visible only for certain users. If that UI control is expensive to create or keep around, you might want to avoid creating it completely rather simply hiding it. In this scenario the variable that holds the UI control may be null and if you attach events to it via AddHandler it would be handy to be able to write AddHandler TheControl?.TheEvent instead of having to manually include the null check via an if-statement as shown above (the AddHandler statement DOES throw NullReferenceException if TheControl variable is null).

So to that end, I hope we can get support for the null-conditional operator in [Add|Remove]Handler statements 👍

CC: @KathleenDollard

@AnthonyDGreen
Copy link
Contributor

AnthonyDGreen commented May 27, 2018

Hi @ericmutta,

If I might jump on this idea a little bit, I see a pattern:

It seems to me that a certain segment of the population would like the language to be more elegant in the presence of null values and do something reasonable rather than crash. I think it would be best to consider that scenario as a whole and examine each case in light of that theme rather than piece-meal.

FWIW, there's a sort of spiritual prior art here in the VB runtime. One of the main advantages to the VB runtime library string functions over the methods defined on String is that they treat null strings the same as empty strings. Arrays get similar benefits:

Dim arr As String() = Nothing
' This loop won't throw because UBound(Nothing) = -1
For i = 0 To UBound(arr)
    Console.WriteLine(arr(i))
Next

I'm not sure whether or how often this is a good thing strictly speaking but it's certainly a convenient thing. Is it something that can be generalized? Should it be?

In the case of expressions classified as values (which does not include event access) such a feature can be defended because the entire expression evaluates as null which is both observable and, to containing expressions or statements, handle-able. But in the case of statements such as AddHandler, Return, or For Each the statement is simply... skipped. That's weird. But if there was some syntax, e.g. AddHandler? or RemoveHandler? one can hardly say that behavior is surprising.

Maybe this should be a thing, what do you think?

@ericmutta
Copy link
Author

@AnthonyDGreen If I might jump on this idea a little bit, I see a pattern:

Hi Anthony, thanks for jumping in, it's good to hear from you! I absolutely agree that doing a sweep of the language and seeing where null-conditional logic can be added would certainly make things more consistent and complete.

@AnthonyDGreen I'm not sure whether or how often this is a good thing strictly speaking but it's certainly a convenient thing.

It is certainly very convenient, and one area I would like to see this "null = empty" logic would be in For Each loops where the collection is null (at the moment an exception is thrown, presumably because the underlying code calls GetEnumerator() on a null collection variable).

@AnthonyDGreen Maybe this should be a thing, what do you think?

We are on the same page. Seeing that null is a (useful though sometimes annoying) fact of life, expanding support for null-conditional execution of code would be a net win 👍 👍

@Nukepayload2
Copy link

@AnthonyDGreen Actually, UBound(Nothing) throws System.ArgumentException at runtime. I have tested this case with .NET Framework 4.6.1 and MSTest V2 1.3.1 . Is this a bug?

Test code:

<TestClass>
Public Class TestNullEmpty

    <TestMethod> 'Passed.
    Public Sub TestStringNullEqualsEmpty()
        Assert.IsTrue(Nothing = String.Empty)
    End Sub

    <TestMethod> 'Failed with ArgumentException.
    Public Sub TestArrayNullSafety()
        Dim arr As String() = Nothing
        For i = 0 To UBound(arr) ' UBound throws ArgumentException here.
            Assert.Fail()
        Next
    End Sub
End Class

@AnthonyDGreen
Copy link
Contributor

@Nukepayload2 , I was wrong! Good catch. I didn't double-check my facts before I posted and this was the sad result :( I conflated two things.

The VB runtime string functions (and the VB language itself in the case of string equality) treats null strings like empty strings (I did try running this code first this time):

Imports System.Console

Module Program
    Sub Main()
        Dim stringThatIsNull As String = Nothing

        ' Prints "0".
        WriteLine(Len(stringThatIsNull))

        ' Prints "0".
        WriteLine(InStr(stringThatIsNull, "Anything"))

        ' Prints an empty string.
        WriteLine(Left(stringThatIsNull, 5))

        ' Prints "True".
        WriteLine(stringThatIsNull = "")
    End Sub
End Module

So Len(<null string>) = 0. I assumed (incorrectly) that the same was true for the array functions. It's clearly not. Len(<null array>) = 0 is also true, but not because the language or runtime treats null arrays the way it does null strings, but because at runtime a null string and a null array are indistinguishable. It has to treat all null values as potentially null strings. So if you wrote your code like the first loop, you'd be null-safe:

Imports System.Console

Module Program
    Sub Main()
        Dim arrayThatIsNull As Integer() = Nothing
        ' Doesn't execute.
        For i = 0 To Len(arrayThatIsNull) - 1
            WriteLine(arrayThatIsNull(i))
        Next

        ' Throws an exception.
        For i = 0 To UBound(arrayThatIsNull)
            WriteLine(arrayThatIsNull(i))
        Next
    End Sub
End Module

But the second way would crash. So I guess that's one place that UBound(arr) <> Len(arr) - 1 :(

I guess that means a little less prior art in the runtime than I said, but the string stuff is still true.

@KathleenDollard
Copy link
Contributor

This is a shortcut for a statement form that is a well-known pattern (contained in a null-checking if). These types of improvements need to be quite compelling.

Also, as noted this has parallels in other places where the null conditional might be imagined outside expressions. If we ever tackle that, it needs to be as a group.

Marked "No Plans"

@KathleenDollard KathleenDollard added the LDM Reviewed: No plans LDM has reviewed and this feature is unlikely to move forward in the foreseeable future label Oct 17, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
LDM Reviewed: No plans LDM has reviewed and this feature is unlikely to move forward in the foreseeable future
Projects
None yet
Development

No branches or pull requests

4 participants