Hacker News new | past | comments | ask | show | jobs | submit login

Having done a lot of python recently, this looks great (and familiar).

But some things feel a little bit rushed:

- In the example "(o is int i || (o is string s && int.TryParse(s, out i))": When reading this statement as a human, o is obviously not an int when it comes down to the TryParse function. But if the 1st part was removed, the 2nd part wouldn't be valid either. I know this is technically how declarations work and I don't have an idea, if there is a better solution for this, but it feels weird.

- The new features of the case statement are nice but the old syntax with the mandatory break is probably worth getting rid of. Especially since all cases share a scope, adding/removing cases is prone to errors. I'd love to see a solution similar to "catch".

- The Deconstruct method is introduced as a "magic name". No interface, no override, nothing... Even the return type is "void". Why not use something like "public out Point(...)" to keep it similar in fashion to the constructor. Other options may be something with "this" (like indexing) or "operator".




I was thrown by the first one you pointed out too, I'm still not sure I understand it.

The page said for type patterns it will "test that the input has type T, and if so, extracts the value of the input into a fresh variable x of type T", so if o is an int it'll be extracted to a fresh int i with that value

But the out syntax in TryParse isn't the new one they mentioned, it's the current one that requires predeclared variables - to be new it'd be out int i or out var i. So i is already declared as an int before this code example? In that case how can the first bit work? Does it not create a "fresh variable" if there's already one in scope with the desired name and type? That could be quite confusing, usual behavior is to compile error if an inner scoped variable conflicts with an outer one, I'm not sure I understand why this should be different.


The expression

    o is int i
is effectively:

    int i; // in the nearest enclosing scope
    (o is int && (i = (int)o)) // in place of an expression
The "fresh" variable `i` is available for use elsewhere. If an `i` already exists in that scope (the block the `if` statement is in), that is a redeclaration of a variable which is a compile-time error.

If the "is" expression evaluates to `false`, the variable `i` will not be assigned, however, it will still be declared. Attempting to use `i` if it is not definitely assigned is a compile-time error. However, you have the opportunity to re-assign it.

Some examples:

    {
      if (!o is int i) throw Exception();
      use(i); // works: i is declared and always assigned
    }

    {
      if (o is int i) { do_something(i); }
      use(i); // compile-error: i declared but might not be assigned
    }

    {
      if (o is int i) { do_something(i); }
      else { i = 0; }
      use(i); // works -- definitely assigned
    }
Thus the example pointed out in the post boils down to this in C# 6:

    int i;
    if (o is int) { 
      i = (int)o;
    } else {
      string s = o as string;
      if (s != null && !int.TryParse(s, out i)) {
        throw Exception();
      }
    }
    // i is definitely assigned here.


> The Deconstruct method is introduced as a "magic name".

Collection initializers already rely on “magic names” (the `Add` method + `IEnumerable`), so I’d say that’s okay. It’s simply a compiler feature, after all.


Deconstruct will be an operator and this is how C# handles other operator overrides, e.g. equality, implicit/explicit type conversions, so this is in line with what developers would expect.


>The Deconstruct method is introduced as a "magic name". No interface, no override, nothing...

There is precedence for this in the form of GetEnumerator and GetAwaiter.




Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact

Search: