
What’s New in C# 7.0 - runesoerensen
https://blogs.msdn.microsoft.com/dotnet/2016/08/24/whats-new-in-csharp-7-0/
======
mintplant
Pattern matching! Tuples! Awesome, C# 7.0 is scratching two itches I've felt
every time I've worked with it.

If only .NET had a suitable cross-platform UI toolkit, then I'd be using it
everywhere. Eto.Forms is a good attempt but I found rather buggy and limited
at this point in development. Avalonia, while further along in terms of
stability and features, doesn't even try to fit in with any platform's look
and feel.

~~~
gsmethells
Feels kinda crazy to see tuples and pattern matching "just arrive" when other
languages have had them for years.

~~~
manigandham
Every language has something missing, but C# has plenty of other great
features so tuples weren't really that big of a problem. There are also plenty
of built-in containers like the Tuple class that can offer the same
functionality.

~~~
MichaelGG
A tuple struct fills nearly zero of the real use cases for language level
tuples. No pattern matching makes it sort of useless.

~~~
adwn
What about returning multiple values from a function without declaring a
dedicated type? That is pretty useful.

------
diego_moita
Sometimes it feels like we're entering a new age of coding. C# is so full of
"features" that code refactoring tools like ReSharper, CodeRush and JustCode
became simply indispensable. It is quite hard to be sure, every time, what is
the most elegant/compact way of doing things.

In the .Net/VisualStudio world, the language tooling (Intelisense, visual
drawing XAML & WinForms, etc) became as important as the language semantics or
the availability of frameworks and documentation.

A long ago there was a fad, known as CASE (Computer Aided Software
Engineering) that advocated doing on programming what CAD did to other
engineering. It seems that were finally getting there.

~~~
pixie_
The most productive I've been as a developer was 20 years ago when I was
creating VB6 apps and components...

~~~
frik
So true. Between 1998 and 2000 VB6 was perfect, you could develop Windows GUI
applications in a rapid manner. The VB6 IDE, the Windows UI guideline, the VB6
API, everything was great. And PlanetSourceCode.com was what was later
SourceForge and nowadays GitHub. When I read about Microsoft's vision for .Net
in 1999 about applications running as a service over the internet (that was
their original idea), I thought that will end the great Windows platform and
started looking for alternatives. Microsoft dumbed VB6, but it took them until
2003 to come up with DotNet with a different implementation, it (C# 1) was
basically a Java clone with small syntax improvements. The biggest fail was to
announce VB.Net and their lackluster Channel9 video that showed the third
party developed the converter wizard (VB6 to VB.Net) that barely worked and
was free only in the lite version. In the meantime I switched to C, C++, PHP,
etc. The web replaced desktop applications in many cases anyway. And nowadays
Android and iOS are important targets too. Sadly, the rapid development never
got that easy to use as it was with VB6 IDE, with HTML4+PHP with Dreamweaver
being almost as fast. But nowadays, the GUI builder aren't as polished and
well integrated and don't support all features of the supported languages - so
many skip the GUI builder and write the GUI code by hand.

~~~
headmelted
Edit-and-Continue is the greatest productivity feature ever added to a
development stack _for the real conditions one would encounter in large
projects_ , and it astonishes me even now how misunderstood it's purpose is.

Consulting has me moving jobs a lot and I encounter a veritable kaleidoscope
of crappy work. Being able to correct minor issues in scope without
restarting, while ten form posts deep in an archaic webforms app with dodgy
"we-didnt-know-so-we-rolled-our-own" state management has been a lifesaver.
Thank you nineties VB team, it's because of you that I'm not rocking back-and-
forth in a padded cell making animal noises today.

~~~
douche
Does Edit-and-Continue ever work? I'm curious, because I'm almost always
forced to build x64-only, because of some libraries we have to use that are
64-bit only, and I've never been able to change code while the debugger is
running. Supposedly it is supposed to work in VS 2015, but I'm not seeing it -
it's always the nasty messagebox saying "Changes to 64-bit applications are
not allowed"

~~~
headmelted
Build 64-bit only or release 64-bit only?

I only ask as I can usually manage 32-bit builds on dev while working, then
just test there's no spooky differences when I target 64-bit.

There are issues with some projects that aren't well documented. E+C on web
apps on local or remote IIS are a no-go (at least to the best of my
knowledge), whereas if you can target IIS express during development it works
beautifully.

I've always found that the juice has been worth the squeeze, for values of
_squeeze_ that only require me to tweak configuration for my dev box.

------
daxfohl
All I want is easy syntax for immutable records, and C#-like a fluent syntax
for .WithPhoneNumber(phoneNumber) etc. Seems like it'd be a cinch to
implement, and for me the single handiest feature from F#.

In F# these are "guaranteed" to be non-null, even though null instances come
up during deserialization all the time. In C# use cases, I don't think the
non-null "guarantee" should be made or implied, just a POCO.

~~~
CyrusNajmabadi
Hi there! C# language designer here.

You can see (and participate in) the discussion on records here if you'd like:
[https://github.com/dotnet/roslyn/issues/10154](https://github.com/dotnet/roslyn/issues/10154)

> Seems like it'd be a cinch to implement

Ah... how i wish that were so :)

~~~
daxfohl
Indeed. While records are my favorite feature from F#, they _could_ be
improved.

I'd love it if record definitions were extensible (even abstractly--sometimes
you want a record with just the data, and sometimes including db-centric "id,
createdTime, etc"), and there should be a way of defining / converting between
them without a ton of boilerplate. That would allow something like:

record DbStuff = { id: int; created: DateTime }

record UserRecord: DbStuff { name: string; ... }

var userRecord = new UserRecord(...)

var userData = userRecord.WithoutDbStuff() // but what would be the reflected
name of this type?

var newRecord = userData.WithDbStuff(3, DateTime.Now)

\----

Sometimes you want to be able to define records

PhotoData{source, metadata, yada, etc, url} and

PhotoDisp{source, metadata, yada, etc, bytes}

without the repetition, and again with an easy way to convert between the two.
(And yes you could simplify the above by using containership but oftentimes
there are cross-cutting things that containership doesn't solve. You really
want a flattening solution.)

Easy integration with C# anonymous classes should be considered too.

C# has always been the more real-world-centric language so I'd hope these
common use cases would be considered.

~~~
JamesBarney
I second this! The ability to easily create intersection types would be
awesome!

------
bifel
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".

~~~
mcintyre1994
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.

~~~
Eyas
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.

------
hvidgaard
I don't like the idea of accessing tuple values by their local name

    
    
        var names = LookupName(id);
        WriteLine($"found {names.first} {names.last}.");
    

It's leaky and should simply use the deconstructing syntax instead.

I'm on the fence about the ref returns. It can lead to some fantastic
performance improvements and enable otherwise unusable datastructures. But is
C# really a language you want if that is important to you. Why not go all in a
use a lib in C or C++ for the critical parts of the code?

I still miss better immutability support, and probably most of all, ability to
declare something at non-nullable.

But that said, the update is a fantastic update to an already good language.

~~~
zamalek
> But is C# really a language you want if that is important to you.

C# is used by hundreds if not thousands of games by ways of Unity and is
therefore demonstrably good enough in this scenario. Furthermore, just because
a language is not C++ does not mean you shouldn't take every opportunity to
give programmers tools to write fast code - at the end of the day I doubt
anyone would be interested in a language that is purposefully slow.

Performance was a core feature of the language (yes, yes, it's managed) from
day 1 with unsafe - it's nice to see further improvements in that department.

Use C++ when you need to, definitely, but when you're copying about 256 bytes
around for a trivial world-view-projection matrix multiplication[1] you have a
problem.

[1]:
[https://github.com/dotnet/roslyn/issues/10378#issuecomment-2...](https://github.com/dotnet/roslyn/issues/10378#issuecomment-223318382)

~~~
jmnicolas
Isn't Unity stuck with C# 2.0 ?

~~~
zamalek
Poked around Google and it seems as though you are correct about Unity;
however, C# tends to use existing MSIL features and doesn't require the BCL
public key token for supporting infrastructure. You can patch in many recent
features in by simply defining the types yourself. A common example of that
would be:

    
    
        public sealed class ExtensionAttribute : Attribute { }
    

Even async/await comprises of a convention and an interface - I haven't tried
it, but _in theory_ using the CLR 2.0 TPL should bring async/await your
project. So far as I know, the feature being used for ref returns has always
existed for C++/CLR so this stuff should work on CLR 2.0 (although you will
need the C# 7.0 compiler).

------
flukus
Out variables seem like a mis-feature, especially when they are adding a
better alternative (Tuples) in the same release.

It's great to finally have tuples, but the c/java style syntax is showing it's
age compared to something like scala which has return type declarations at the
end, which I find much more readable.

Literal improvements will be a godsend for writing database migrations.

Ref returns and locals look like a source of constant headaches. It's much
harder to reason with code when the data can be modified by random others bits
of code. Currently I can look for all references to an array to find
everywhere that modifies it now I can't.

~~~
CyrusNajmabadi
Hey there, C# language designer here.

> Out variables seem like a mis-feature, especially when they are adding a
> better alternative (Tuples) in the same release.

Out variables are really great when working with existing code that predates
Tuples (for example, the entire BCL). We don't just introduce language
features that will only work well for new code. We also want to make the
experience of coding against existing APIs feel better.

> Ref returns and locals look like a source of constant headaches.

That 'headache' has to be weighed against the needs of some parties to write
very fast code and to have as little overhead as possible. ref returns enables
a much more pleasant coding experience that enables these sorts of speedups in
these specialized scenarios.

> It's much harder to reason with code when the data can be modified by random
> others bits of code.

There is a simple solution to this of course, don't expose your data through
ref returns :)

For many (likely most) developers, ref returns simply won't be something that
ever hits their radar. Similar to stackallocs and other esoteric features,
this is really a targeted improvement for people who really like working in
.Net but are finding it hard to squeeze out essential perf in some core
scenarios.

The .net runtime supports this features fantastically. We wanted to make it
available to people who like the power and safety of C#, but occasionally need
some low level hooks to get that extra perf boost as well.

~~~
flukus
> That 'headache' has to be weighed against the needs of some parties to write
> very fast code and to have as little overhead as possible. ref returns
> enables a much more pleasant coding experience that enables these sorts of
> speedups in these specialized scenarios.

I'm worried this could result in a loss of focus, you can't make everyone
happy all the time. c# is a fantastic language to develop applications in (web
or desktop) but it's never going to be the fastest language around or be a
systems level language. For me some of the other features listed here (like
immutable records) would be a much better fit for where c# excels already.

~~~
pbz
Well maybe you don't care, but there are folks that do (I'm on that list) ;-)
I want the speed of C but the safety of C#.

There are many pieces of code that you probably use every day that go to great
lengths to squeeze as much power as possible. Why not help these folks help
us? I'm glad there is more focus on performance because we all benefit.
(Hoping to see more on the CLR side.)

~~~
flukus
Why c# and not rust or something similar that has a lower level focus?

~~~
pjmlp
Because some of us believe in GC enabled systems programming languages, as
done already at Xerox PARC (Cedar), DEC (Modula-2+/Modula-3), ETHZ (Oberon,
Active Oberon, Component Pascal) and MSR (Sing# / System C#).

What is missing is a company like Microsoft to push it down to mainstream
developers, at all costs, like it happen to all luddites.

------
alkonaut
I still lack some kind of sum type that is always checked for exhaustiveness.
When I'm creating a class hierarchy for a domain, 9 times out of 10 I'd rather
have a simple sum type. When I have to make a class hierarchy in C# anyway
(because there are no simple sum types). I'd like to be able to write
something like this, and have this match/switch _fail_ to compile if I add a
new type of shape.

    
    
        match(shape) 
        {
          case Rectangle r
            return r.With*r.Height;
          case Circle c
            return pi*c.Radius*c.Radius;
        }

~~~
macca321
I'm just going to pimp my library (github.com/mcintyre321/OneOf) which lets
you pull this off in c#. I'm quite sad it's needed though, it would be great
to have it in the language.

~~~
alkonaut
That's very neat, it should be pretty easy to sugar something like that into
the compiler with no CLR changes.

------
dleslie
It looks like they've added a lot of rope to hang ourselves with typos.

    
    
        int myvar, I;
        foo(out int mvar); // oops, not myvar; maybe caused by a refactor?
        bar(out *); // oops, not I; was up too late coding
    

And so on. Things like this would be easily missed when reading code at-a-
glance, and it's this sort of bug that arises often in languages that allow
implicit declaration of variants.

~~~
CyrusNajmabadi
Hi there, C# language designer here.

I don't really see your example in that way. Let's start with the latter one
first:

> bar(out *); // oops, not I; was up too late coding

I'm not sure how this situation is any differnet from any other case where you
need to pass some variable, and you pass the incorrect name. This is already
possible all over the language. For example, you might have written "bar(out
J)" when you meant "bar(out I)". As usual, the recommendations here are to use
strong types and good names to help keep your code clear in intent and to
allow the compiler to tell you when something seems awry.

Now let's look at your first example:

> foo(out int mvar);

This version immmediately screams at me that something is happening that
requires my attention. First off, just the presence of 'out' is an immediate
call that this is not a normal call. Nearly all calls pass by value, so
out/ref stick out like a sore thumb (esp. in an IDE that classifies them
accordingly). Second, the "out int" is another large indicator that this is
doing stuff very special.

Finally, i'd point out that the mispelled name problem is really no different
than what you might experience today with code like:

    
    
      int myvar;
      ...
      // much later and indented a bit ...
             int mvar;
             Foo (out mvar);
    

Here you've unintentionally introduced a new variable that you may or may not
have intended to. Without a collision, there's no way to tell.

> it's this sort of bug that arises often in languages that allow implicit
> declaration of variants.

No implicit declarations are allowed. All declarations are explicit. We just
don't force you to have to declare in one location and use in another. This is
a pattern that many people hate, and which goes against a desire to
immediately declare and initialize, and thus not have to track down how a
variable is written to.

~~~
dleslie
> I'm not sure how this situation is any different from any other case where
> you need to pass some variable, and you pass the incorrect name.

It's a wildcard. Passing in any other variable name would ideally raise an
error about the use of an undeclared variable or a mismatched type. The use of
a wildcard disposes of those errors.

> the mispelled name problem is really no different than what you might
> experience today

Not quite; your modified examples includes two declarations on their own
lines. Being on their own line gives them greater visual presence at-a-glance
than the new syntax which buries the declarations within a parameter list.

Worth noting is that my trivial example managed to confuse at least one reader
who was unable to see the issue[0].

> All declarations are explicit.

While true, you've muddied the lines a little by moving declarations into the
syntax of other expressions. Where previously a declaration sat on its own
line or at the beginning of an assignment, they may now be peppered throughout
the syntax in ways that are not so easy to observe at-a-glance.

0:
[https://news.ycombinator.com/item?id=12356681](https://news.ycombinator.com/item?id=12356681)

------
mhomde
Wow, lot's of great stuff! I especially like local functions, I'm all for
breaking up complicated methods into several methods to simplify and clarify
syntax... but it always felt weird that all those methods where on an class
scope even though they only were relevant for that method.

This resulted in many cases that those methods needed to be broken out into
their own class... which is the way to go some times but felt like a bit of
overkill in others

~~~
zubspace
I mostly agree, but until now I just created private helper methods and went
on with my life. If there are many of them, I put them into a #region. There
are alot of cases where such helpers can be used from two separate locations
and they also increase discoverability for other devs.

If you really wanted to, wasn't it already possible to create anonymous
delegates/lambdas locally? The only improvement I see with local named methods
are readable stacktraces.I have to admit, the mangled names are a major
headache for me (and the stacktraces starting at lamda invocations, missing
the 'original' stacktrace. I'm looking at you Parallel.Foreach!)

------
headmelted
I've been really excited about this release, but also a little disappointed
that we still don't have proper object composition.

I'd be happy with either extension methods with state or adding traits to the
language as a separate feature. I understand the reasons it was decided
against in C# 4, but its hard to preach composition over inheritance when the
framework is philosophically against it, and it runs contrary to how the whole
framework is structured (single inheritance from Object). But it still irks my
fussiest self.

You can get something that sort of gets mostly there with
ConditionalWeakTable, but its not endorsed by the vendor, and in the
experience I had with it while trying to create a mixins module for C# a few
years back, makes the GC leak like a sieve at scale.

------
ricardobeat
What's the point of

    
    
        GetCoordinates(out var x, out var y)
    

over destructuring assignment like

    
    
        var x, var y = GetCoordinates();
    

the former looks completely backwards.

~~~
reitanqild
Wondered about this myself, is this something that is needed for backwards
compatibility or something?

IMO fishing parameters back from functions is one of the things I do not miss
from old C.

Anyone has anything to add to masklinn's explanation?

Edit:

found CyrusNajmabadi's comment here:
[https://news.ycombinator.com/user?id=CyrusNajmabadi](https://news.ycombinator.com/user?id=CyrusNajmabadi)

------
insulanian
I just realized that I don't remember I ever saw a C# rant on HN and very very
rarely in general. It must have the highest usage/complaints ratio in the
industry. Now that I said that I'd love to see the languages ordered by that
ratio!

~~~
nenreme
Maybe not exactly what you want but it is a "% of developers who are
developing with the language or tech and have expressed interest in continuing
to develop with it".

[http://stackoverflow.com/research/developer-
survey-2016#tech...](http://stackoverflow.com/research/developer-
survey-2016#technology-most-loved-dreaded-and-wanted)

------
rmsaksida
Since we're all chipping in with features we'd like to see in C#, here are
mine: anonymous classes - with actual class bodies, that can implement
interfaces - and traits.

~~~
voltagex_
Can you give an example of how C# would be different with anonymous classes?

Googling that term just gives a whole lot of references to inner classes in
Java.

~~~
flukus
One I've come across is binding a command to a button. With anonymous inner
classes you can have something like:

    
    
      myButton.AddListener(new Command {
        bool enabled() {
          return false; //more logic goes here
        }
        void onClick() {
          //do something
        }
      });

~~~
voltagex_
I'm not convinced that gives you that much more over using a delegate.

~~~
flukus
It doesn't. It's a nice to have feature, not a world changing one.

------
DropbearRob
Im so excited by this in particular

"Switch statements with patterns

We’re generalizing the switch statement so that:

•You can switch on any type (not just primitive types)

•Patterns can be used in case clauses

•Case clauses can have additional conditions on them

Here’s a simple example:

switch(shape)

{

    
    
        case Circle c:
    
            WriteLine($"circle with radius {c.Radius}");
    
            break;
    
        case Rectangle s when (s.Length == s.Height):
    
            WriteLine($"{s.Length} x {s.Height} square");
    
            break;
    
        case Rectangle r:
    
            WriteLine($"{r.Length} x {r.Height} rectangle");
    
            break;
    
        default:
    
            WriteLine("<unknown shape>");
    
            break;
    
        case null:
    
            throw new ArgumentNullException(nameof(shape));
    

}

There are several things to note about this newly extended switch statement:

•The order of case clauses now matters: Just like catch

clauses, the case clauses are no longer necessarily disjoint, and the first
one that matches gets picked. It’s therefore important that the square case
comes before the rectangle case above. Also, just like with catch clauses, the
compiler will help you by flagging obvious cases that can never be reached.
Before this you couldn’t ever tell the order of evaluation, so this is not a
breaking change of behavior.

•The default clause is always evaluated last: Even though the null case above
comes last, it will be checked before the default clause is picked. This is
for compatibility with existing switch semantics. However, good practice would
usually have you put the default clause at the end.

•The null clause at the end is not unreachable: This is because type patterns
follow the example of the current is expression and do not match null. This
ensures that null values aren’t accidentally snapped up by whichever type
pattern happens to come first; you have to be more explicit about how to
handle them (or leave them for the default clause).

Pattern variables introduced by a case ...: label are in scope only in the
corresponding switch section."

------
platz
I wish they did not call it pattern matching. Instead this is more like
Destructuring combined with Switch. the guarantees are different

~~~
edgyswingset
"Full" pattern matching, as one would expect from F# for example, wouldn't be
something I could expect from C#. For example:

    
    
        type Tree<'a> = Empty | Node of 'a * Tree<'a> * Tree<'a>
    

There is no way to express this directly in C#. Thus, I am "locked out" of a
whole class of Pattern Matching that I would get with F#.

But that doesn't mean that C#-style pattern matching (really, Is-patterns and
a Type Switch) don't fall under the umbrella of Pattern Matching. You can
define tuple patterns that you can match on with the switch statement in C#,
which is every bit a form of pattern matching as matching on a particular case
of a union type.

~~~
platz
> C#, is every bit a form of pattern matching as matching on a particular case
> of a union type.

I'm afraid that is not the case, because in the current C# version, the
compiler does nothing help you determine if you've matched all the
possibilities. (it is not exhaustive)

Exhaustiveness is important because it gives warning or an error to help you
refactor and modify code without fear of breaking other code. Why engage in
all the static typing business if the compiler is not going to help you
refactor things?

This is important in the same way that adding a subclass requires exhaustively
supplying all the abstract methods (true pattern matching is "dual" to adding
a subclass; you use the compiler feedback to direct what actions to take next.
you can't do this with the Switch based formulation because you don't get any
feedback from the compiler if you missed cases or have refactored other code
to add cases (for example, adding/refactoring abstract methods in a base class
provides feedback on where to add/update methods in the subclasses)

~~~
edgyswingset
Yes, and because of the lack of record and union types in C#, it's (probably)
nigh-impossible to pull off exhaustive pattern matching. However, this is
_still_ pattern matching - you can define tuple patterns which decompose the
data, much like you can in F#; just with some different syntax.

I agree with you that exhaustiveness is incredibly important, and I really
hope that it can be done in C# some day. However, record and union types are
also a piece of this puzzle.

~~~
macca321
You can achieve something similar via a library:
[https://github.com/mcintyre321/OneOf](https://github.com/mcintyre321/OneOf)

I also think language level support for this would be a killer feature, it
turns OOP and the requirement to use polymorphism to guarantee strategy-per-
type on it's head.

------
hacker_9
CSharp once again showing it is the best language in terms of features and
improvements. Great job all round by the designers.

~~~
wsc981
I'd say Swift is more or less in line with C# with regards to functionality.
Many new features added to C# seem to already exist in Swift. Swift could use
some async/await mechanism en sometimes it's a bit annoying to typecast simple
types for calculations, but otherwise Swift is a really sweet language to work
with.

Nonetheless C# is a nice language. I enjoy writing C# code, just as I enjoy
writing Swift code.

~~~
hacker_9
I'd disagree with you to be honest. Swift is still in it's infancy, and while
it copied many of it's features from CSharp, it did so poorly and even now
they continue to make breaking changes to their standard with each update.

~~~
72deluxe
Yes I have noticed this. It is interesting observing the changes to Obj-C just
to keep up with the developments in Swift-land, when Swift itself is changing
so frequently.

------
MichaelGG
Will C# ever start to increase type inference to make it less verbose? Local
functions are ok. But why not lambda syntax+inference? Probably still because
of making quoted code have ambiguous syntax. But this ends up adding noise and
increases the barrier on deciding when to put code into a tiny function.

When using C# I'm constantly noticing how much extra code there is. It's just
frustrating, and I don't feel like I'm getting a unique benefit in return
(unlike, say, in Rust). I know F# would be more concise and work just as well.
But I know it's not easy extending the existing design decisions.

(The tuple story is sad, eh? This was F#'s original design, using structs.
Then they made peace with the strange heap allocated framework tuple. And
now...? No tuple interop?)

Anyways, congrats, C# definitely is the best general dev language commonly
accepted at a large amount of companies, especially when factoring in tooling.

~~~
snaky
> how much extra code there is. It's just frustrating, and I don't feel like
> I'm getting a unique benefit in return (unlike, say, in Rust)

Actually you are getting a benefit in return (unlike, say, in Rust) - the
libraries. Verbose syntax is less complex in regard to writing the code, so
more developers write the code. I have no scientific evidence to provide about
the fact, but observations speak for themselves. Compare the quantity of
libraries available for Rust and yet another 'overly verbose' language, Go. We
can find Go wrapper for almost anything, and often a few to chose from.

~~~
mcsofake
> Compare the quantity of libraries available for Rust and yet another 'overly
> verbose' language, Go. We can find Go wrapper for almost anything, and often
> a few to chose from.

Rust was stabilized last year, while Go was released six years ago. It is
simply wrong to say that Go has a larger quantity of libraries because it is
more verbose. IMHO the Rust ecosystem is surprisingly comprehensive for a
language its age.

------
iliketrains
Nice! I was also hoping for some sort of guaranteed not-nullable type and
immutable classes. Maybe in C# 8.

~~~
daxfohl
F# has guaranteed not-nullables, except when you do default<NotNullableThing>
it returns null. That said, it generally works well in well-defined situations
but any time there's deserialization it's often a bigger mess than just never
assuming not-null.

I think it's a problem that needs to be solved in the CLR, not C#, and my
guess is it probably never will be.

------
adrianratnapala
Does the pattern matching also statically check that you don't try to use a
failed match.

What if instead of

    
    
         if (o is null) return;     // constant pattern "null"
         if (!(o is int i)) return; // type pattern "int i"
         WriteLine(new string('*', i));
    

somone forgot to return, as in

    
    
         if (!(o is int i)) {
             // do something
         }
         // incorrectly try to use `o` as an int
         WriteLine(new string('*', i));
    

Rust uses the special `match` syntax to avoid this. But I suppose C# 7.0 also
analyse the structure of the `if`s and `return`s to acheive the same effect
more flexibily.

And if it can do that, could that they also start phasing in statically-
checked null avoidance?

~~~
Eyas
This was a concern of mine as well, as you can see in the discussion of scope
changes for pattern variables [1], the C# checks for definite assignments
handle this. So:

    
    
        if (o is int i) { f(i); }
        use_int(i); // <-- error: variable `i` is not definitely assigned
    

But then if you have:

    
    
        if (o is int i || (o is string s and int.TryParse(out i)) use_int(i);
    

We know that `i` is definitely assigned at that location.

[1]:
[https://github.com/dotnet/roslyn/issues/12597](https://github.com/dotnet/roslyn/issues/12597)

------
billpg
I tend to avoid using switch/case for quite irrational reasons. The break
statement just looks wrong and I can't layout the code in a way I find at all
pleasing. I'll almost always use a sequence of else if conditions instead.

In my ideal world, it'd look like...

    
    
        switch (x)
        {
            case 1:
            {
                /* ... */
            }
            case 2:
            case 3:
            {
                /* ... */
            }
        }
    

And before anyone points it out, switch is the same as else if when the thing
being compared is a simple local variable.

~~~
ivanca
Same here, plus I think the parent brakes are noise, can the compiler just
know that the switch statement ends on last case?

~~~
breakingcups
Not with nested switch statements.

~~~
ivanca
...I don't want to ever have to work with your code.

------
statictype
Like everything except "Ref returns and locals".

I don't think I've ever seen a case where I wished for that feature.

All the other things - tuples, anonymous out vars, pattern matching - I've
found myself wanting quite often.

~~~
taspeotis
I believe ref returns are a performance feature. They seem a bit like move
constructors in C++ in that they can save you a copy? But I am not confident I
understand the nuances of move constructors to compare them properly.

~~~
johncolanduoni
They don't move anything, they are either pointers to a value on the stack or
to a field in an object on the heap. The first is more like how standard C++
references are usually used, and the second are interior pointers like Go has.
No code gets executed to make them happen, and no data is copied.

------
joeax
Since C# is starting to look more like JavaScript (i.e. local functions) and
vice versa (i.e. arrow aka lambda expressions). I'm going to throw my pocket
change into a couple features I've been growing old hoping to see:

1\. Regex expressions just like Regexp in JS:

    
    
        /^\s+$/.IsMatch("  "); // or
        Regex r = /^\s+$/;
    

2\. DateTime expressions, similar to regex, something like:

    
    
        DateTime clockTowerLightning = #1955/11/12 10:04PM#;

~~~
will_hughes
I'll take DateTime literals, for sure. But only if they require using ISO date
format. A slight munge of omitting the timezone to mean 'system local
timezone' would be fine too.

My main objection is over the month/day confusion with other formats. Is that
November, or December?

~~~
douche
ISO-serialized datetimes are the only serialized datetimes. At least if you
want to avoid painfulness. Similarly, UTC is good. If there's one thing I've
learned, it's that, for my own sanity, datetimes should be localized and
formatted at the last possible instant, just before a human has to see them,
and kept in a consistent, canonical form at all other times.

------
sergiotapia
I thought pattern matching in C# would look like pattern matching in Elixir.
For example, two functions with different signatures run based on the
parameters you give it.

    
    
        def hello("sergio") do
          IO.puts "Hello Sergio"
        end
    
        def hello(name) do
          IO.puts "Hello #{name}"
        end
    
        ---
    
        hello("sergio") => "Hello Sergio"
        hello("mia") => "Hello mia"

~~~
daxfohl
Isn't that more an example of `multiple dispatch` rather than `pattern
matching`?

~~~
muhmi
Both :) You can pattern match in function head (definition)

[http://elixir-lang.org/getting-started/pattern-matching.html](http://elixir-
lang.org/getting-started/pattern-matching.html)

------
pingec
Pattern matching, tuples, local functions... feels like someone was inspired
by how Typescript turned out :)

~~~
hvidgaard
Or just looking at F#.

------
Sir_Cmpwn
In the binary literal proposal there is also some unresolved discussion about
octal support, chip in if you like:

[https://github.com/dotnet/roslyn/issues/215](https://github.com/dotnet/roslyn/issues/215)

~~~
Jtsummers
I wish more languages would take a radix-agnostic approach (at least to a
reasonable limit). What's wrong with:

    
    
      2#1010101
      8#75342
      1341235
      10#13451
      16#feb1300
    

radix#value, from erlang.

Or:

    
    
      #b10101
      #xfefe
      #o7777
      #36rSSSS
    

From common lisp.

Customize in some fashion for C# and its current notations for hex and (now)
binary literals.

    
    
      036rSSSS
      02r10101
      0b10101 // this and above are equal

~~~
Retra
The reason people don't do this is:

1\. Our current base notation is already familiar and widely used. Nobody is
confused about 0xA vs 0b10.

2\. Nobody will really use those other bases, so you're adding obscure
features that muddle your syntax.

------
markatkinson
Wow! These are some really impressive features. I started learning F# a few
months back and I can see some interesting parallels. Very excited to see
Tuples and Pattern Matching.

------
rat87
Is 6.0 widely used in industry/open source?

~~~
__s
At my work I do a lot with 3.5 still. We need to upgrade our version of
devexpress. More recent projects use MVC with .Net 4.0 or 4.5 (but our IT guy
is nervous about installing such new runtimes on the servers that don't have
4.5 already..)

We use VS2012

~~~
wluu
You can easily use c# 6 with .NET 3.5. I know because we do!

You just need to upgrade VS to 2015. Language version has nothing to do with
framework version!

------
Achshar
Noob question, why tuples? I mean why 3?

~~~
bsaul
t doesn't mean three, it works with 2 or more.

------
Nano2rad
Microsoft is mixing object oriented and functional in c#. This is not right,
not only in case of Microsoft but also Python etc. They are fundamentally
different and self-contained and need to be kept separate.

------
davidgrenier
Unfortunately, C# can flail about adopting F# features yet the best feature of
F# is something C# can never get:

Dependency Order Compilation

~~~
daxfohl
Except it does. And F# doesn't. F# just has _in-order_ compilation. C# has an
extra step to determine dependencies and compile them in dependency order.

The only thing for F# is that F# allows the user to determine the compilation
order and show it in Visual Studio.

~~~
davidgrenier
Sorry I wasn't clear, I meant in the following sense:

"One of the most common complaints about F# is that it requires code to be in
dependency order."

In light of your statement I understand the way I put it made little sense.

------
ComputerGuru
My instant concern: the variable names used by function declarations have
become part of the API. If you use var to match the library producer's naming
for tuple members, a rename of the variable in the definition will break your
code, as the name you previously used will no longer exist!

Old example:

    
    
        bool Hello(string name) {...}
        var myName = "world";
        Hello(myName);
    

The developer can change the name of variables in the prototype to be more (or
less!) descriptive without a problem - they're decoupled.

New example:

    
    
        (string interjection, string name) GenerateGreeting() {...}
        var greeting = Greeting();
        Console.Write("{0} {1}", interjection, name);
    

Now should the author change "interjection" to "greeting", _ your code won't
compile _ and in a way that you likely didn't expect.

~~~
bhrgunatha
I assumed they are still decoupled and just shorthand for:

    
    
      string interjection;
      string name;
      (interjection, name) =  GenerateGreeting() {...}
    

I can't believe the C# team would introduce something so brittle into the
language. If you're right, that a serious concern.

