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

Is-expressions w/ patterns: Why is var allows nulls through? #981

Closed
HappyNomad opened this issue Oct 7, 2017 · 7 comments
Closed

Is-expressions w/ patterns: Why is var allows nulls through? #981

HappyNomad opened this issue Oct 7, 2017 · 7 comments

Comments

@HappyNomad
Copy link

This doesn't break in the debugger:

static void Main( string[] args )
{
	string something = null;
	if ( something is string s )
		Debugger.Break();
}

But this does:

static void Main( string[] args )
{
	string something = null;
	if ( something is var s )
		Debugger.Break();
}

The behavior is different even though, when I hover the mouse pointer over var in Visual Studio, it says class System.String like in the first snippet.

I like writing if ( SomeExpression is SomeType something ) instead of just if ( SomeExpression != null ) since then I don't have to repeat/reevaluate SomeExpression inside the if block. But sometimes SomeType is a mess of nested generic types that I don't want to repeat writing either. This language behavior, however, precludes me from using is var. I'd appreciate an explanation as to why it was decided to make the feature behave this way.

@sharwell
Copy link
Member

sharwell commented Oct 7, 2017

📝 Closely related to #792

@HaloFour
Copy link
Contributor

HaloFour commented Oct 7, 2017

I don't believe that issue actually answered the core question, though.

The two syntaxes represent different patterns. You have the "type pattern" which matches only on non-null values of the specified type, and then you have the "variable pattern" which matches anything. You have a separate "null pattern" which matches only null, but unfortunately there isn't a corresponding "non-null pattern" (yet).

The current behavior doesn't make a lot of sense in the context of pattern matching as it exists in C# 7.0, but with recursive patterns it will make more sense. Then the var pattern can be used to extract a value from within a deeper pattern and into a variable:

Person person = GetPerson();
if (person is Student { Grade is 4.0, FirstName is var firstName }) {
    Console.WriteLine($"{firstName} is a perfect student!");
}

The pattern matching proposal is here: https://github.com/dotnet/csharplang/blob/master/proposals/patterns.md

@alrz
Copy link
Member

alrz commented Oct 10, 2017

The thing is that null can be a valid value if you just want to bind a variable to an expression within recursive patterns or otherwise as a catch-all case.

I've proposed #306 to make it explicit when you want to null guard that variable,

if ( obj is var x? )
if ( obj is var (x?, y) )

@gafter
Copy link
Member

gafter commented Oct 11, 2017

In the next update to pattern-matching in C#, we will be adding support for a property pattern. For example, if you have a object o, you would be able to test if it is a Point whose X value is 3, and save its Y value into a variable y by writing

if (o is Point {X is 3, Y is var y}) ...

(syntax not necessarily final; we might use : instead of is)

If the input is already of the type you're testing, you can omit it and that type is implied. For example, if you have a variable p of type Point, you would write

if (p is {X is 3, Y is var y}) ...

You can also provide an identifier if you want to refer to the matched entity later:

if (GetPoint() is {X is 3, Y is var y} p) ...

The semantics of a property pattern is that it first tests if the input is non-null, and then if it is non-null it also matches the named properties against the provided patterns. You can have any number of nested property/pattern pairs. In this example, if GetPoint() returns type Point?, then p will be of type Point (stripping nullable).

As a side-effect, you get a non-null pattern "for free", because "zero" satisfies "any number":

if (GetPoint() is {} p) // if the returned point is not null...

Given that there is pattern {} that acts exactly like var but doesn't match null (and conveniently strips Nullable<> from the type when it is present), we are unlikely to add any further way to do the same thing.

@HaloFour
Copy link
Contributor

@gafter

Neat. Any meeting notes or proposal updates coming soon?

@gafter
Copy link
Member

gafter commented Oct 11, 2017

We have not had any LDM meetings on pattern-matching recently.

@HaloFour
Copy link
Contributor

HaloFour commented Oct 12, 2017

@gafter

Proposal updates would be nice, then. The existing proposal is quite outdated and doesn't even reflect the behavior or syntax of what was released in C# 7.0. For example it still refers to the narrow scoping rules and custom operator is. In fact I don't see a slimmed down proposal or spec changes for "type-switch" either.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants