none
.NET Framework - What's New in C# 7.0 RRS feed

  • General discussion

  • The team is now buttoning down development on C# 7.0, and Visual Studio 2017 Release Candidate is implementing virtually all of the new features. Come along as Mark Michaelis explores each of the new features in detail, including deconstructors, pattern matching with is and switch, local variables, and more.

    Read this article in the Connect(); issue of MSDN Magazine


    Wednesday, November 16, 2016 7:41 PM
    Owner

All replies

  • Figure 5 / Type casting with Pattern Matching contains this:

    if (((storage is UsbKey usbDrive) && usbDrive.IsPluggedIn)   {     usbKey.Unload();

    I assume the last line should read usbDrive.Unload() instead.

    Wednesday, November 16, 2016 11:21 PM
  • Hi DarKlajid,

    Yup.. .that is correct.  Thanks for pointing it out.  I will try to get it updated.

    Mark

     
    Friday, November 18, 2016 5:30 AM
  • By the way, do note that there is an extra parenthesis in the if statement that should not be there.

    if (((storage is UsbKey usbDrive) && usbDrive.IsPluggedIn)
    ---^

    Thursday, December 1, 2016 6:35 AM
  • Even more ((()(())())) brackets with tuples. They make C# less readable. Like C/C++ typecasts in former days or LISP.
    Friday, December 2, 2016 10:07 AM
  • This link mentioned at the beginning of the article is currently dead: http://itl.tc/CSharp7FeatureSummary
    Wednesday, December 7, 2016 8:31 AM
  • When I first read of these changes, my concern was that it felt like C# was turning into JavaScript and this update does little to convince me otherwise.

    Most of these enhancements seem to do little to improve the readability and maintainability of the code, mainly to make the language more compact.

    But one egregious example is in the new pattern matching section. The example argues for the use of type matching in order to call Eject() on a collection of derived classes. It does this by essentially iterating through a list of known types and calling Eject() when it's known that method exists.

    This is very bad OOP design. The actual better way is to define an interface for storage types which can do eject (say IEjectableStorage) and for devices which can eject, inherit that interface. Then rather than going through a set of type matches, you can do the much simpler: 

    if(storage is IEjectableStorage) ((IEjectableStorage)storage).Eject();

    While I like the concept of the type matching switch, there's serious potential for confusing code. Consider this:

    enum Test { a, b, c };

    Test t = Test.a;

    switch(t)

    {

    case int32: break;

    case Test.a: break;

    case Test.b: break;

    default: /* extra bad... */ break;

    }

    The expression bodied functions is really surreal to me... you're replacing { ... } with => ... ONLY when there's exactly one line of code. Otherwise, you've made the code larger to no obvious advantage. Either way, this really just obscures the code by introducing an arbitrary syntax that really offers little benefits (ok - I'm actually lost as to what benefits it offers at all).

    Seriously.. here's one of your examples:

    void Dispose() => File?.Delete();

    Here's what it saved us from:

    void Dispose() { File?.Delete(); }

    And if you needed two statements, you'd end up with this monstrosity:

    void Dispose() => { File?.Dispose(); File?.Delete(); }

    which is LONGER than the normal form.

    The entire concept of inline declaration is worrisome. It IS very convenient - but it's also significantly less clear to read. It's one of the reasons, for example, that inline assignments in conditionals was removed; to make sure the code wasn't being tricky and in the process, cause problems.

    A similar argument could be made for 'naked' tuples. Given the new 'inline' class member assignments during construction, the value of tuples other than creating obscure code for lazy programmers is questionable.

    Example:

    (int A, int B, int C) GetTuple() { return (1,2,3); }

    var tuple = GetTuple();

    or

    class Triplet { public int A {get; set;} public int B {get; set;} public int C {get; set;} }

    Triplet GetTriplet() { return new Triplet{A=1, B=2. C=3}; }

    var triplet = GetTriplet();

    In the first case, I get something which may be what I want.. or it might be anything.. so now I have to do a ton of checking to validate the tuple's content's types, number, range, etc. To show why: consider this change:

    object GetTuple() { return (1,2,3); }

    Perhaps if there were a way to declare a "named" tuple as a type:

    Tuple (int A, int B, int C) Test;

    then this would feel less 'seat of the pants'.

    In the second case, I know EXACTLY what I'm getting and don't need to validate anything other than the actual values I want to look at.

    I like that with each version of C# things improve and many of the new features really do make life better for C# programmers, but sometimes it feels like new features are added either to make C# less and less type safe (thus my JavaScript comment) or more and more 'gimmicky', replacing simple albeit verbose code with more obscure and more compact code.

    I know we don't have to use these features, but people will and this brings back nightmares of a recent kind, when one of our coders stuck in what he thought was a brilliant bit of 'easy to understand' code.. consisting of endless tail-chained function calls, mutable classes and surreal delayed inline code. It took three of us half an hour to untangle and decypher.

    I wish future changes to C# focused on making the code readable, testable and manageable rather than helping bad programmers write 'cowboy code'.

    Monday, December 12, 2016 7:25 PM
  • Thanks for taking the time to comment Jeff. This is great and I think your comments make some valid points that I will try to respond to in order.

    But one egregious example is in the new pattern matching section. The example argues for the use of type matching in order to call Eject() on a collection of derived classes. It does this by essentially iterating through a list of known types and calling Eject() when it's known that method exists. This is very bad OOP design. The actual better way is to define an interface for storage types which can do eject (say IEjectableStorage) and for devices which can eject, inherit that interface. Then rather than going through a set of type matches, you can do the much simpler: if(storage is IEjectableStorage) ((IEjectableStorage)storage).Eject();

    Absolutely!  If you can solve a problem using polymorphism rather than pattern matching you should.  Pattern matching should only be used when polymorphism is not a viable option - i.e. when the types don't share a common base class or interface and can't be modified (for example when the code isn't available) to do so.

    While I like the concept of the type matching switch, there's serious potential for confusing code. Consider this: enum Test { a, b, c }; Test t = Test.a; switch(t) { case int32: break; case Test.a: break; case Test.b: break; default: /* extra bad... */ break; }

    Indeed... it is still possible to write confusing code and you have a great example of that.  The same would be true with an if statement.  Unfortunately, I don't have a recommendation besides don't do that.  Hopefully, there aren't too many examples of developers wanting to do this for functional reasons and mistakenly falling into ambiguities.

    The expression bodied functions is really surreal to me... you're replacing { ... } with => ... ONLY when there's exactly one line of code. Otherwise, you've made the code larger to no obvious advantage. Either way, this really just obscures the code by introducing an arbitrary syntax that really offers little benefits (ok - I'm actually lost as to what benefits it offers at all). Seriously.. here's one of your examples: void Dispose() => File?.Delete(); Here's what it saved us from: void Dispose() { File?.Delete(); } And if you needed two statements, you'd end up with this monstrosity: void Dispose() => { File?.Dispose(); File?.Delete(); } which is LONGER than the normal form. The entire concept of inline declaration is worrisome. It IS very convenient - but it's also significantly less clear to read. It's one of the reasons, for example, that inline assignments in conditionals was removed; to make sure the code wasn't being tricky and in the process, cause problems.

    :)

    It certainly isn't anything <g class="gr_ gr_1735 gr-alert gr_gramm gr_run_anim Punctuation only-ins replaceWithoutSep" data-gr-id="1735" id="1735">overwhelming</g> is <g class="gr_ gr_1734 gr-alert gr_gramm gr_run_anim Punctuation multiReplace" data-gr-id="1734" id="1734">it.</g>  In the case of C# 7.0's changes, it was more a matter of completing the C# 6.0 scenarios on when to make it available.  I don't have any advice besides don't use it if you don't like <g class="gr_ gr_3338 gr-alert gr_gramm gr_run_anim Style replaceWithoutSep" data-gr-id="3338" id="3338">it,unfortunately.</g>  It is a minor nicety at best and barely moves the needle on the - eliminate ceremony goal.  Personally, I find myself using it for the crazy simple implementations.

    I'm going to post what I have here now and then <g class="gr_ gr_2754 gr-alert gr_gramm gr_run_anim Grammar multiReplace" data-gr-id="2754" id="2754">add</g> <g class="gr_ gr_2764 gr-alert gr_gramm gr_run_anim Grammar replaceWithoutSep" data-gr-id="2764" id="2764">more</g> later.  In summary, I hear you. Language design is hard and there is an extremely challenging balance. Perhaps there are some items that are not compelling for some but overall, I believe C# 7.0 is a significant improvement over all.  It just isn't in the same league as improvements like generics or LINQ, unfortunately.

    Wednesday, December 14, 2016 6:14 PM
  • The ref variable can be modified point to other location or not? 

    Following paragraph reference from '.NET Framework - What's New in C# 7.0':

    Second, ref locals are initialized to a certain storage location in memory, and can’t be modified to point to a different location. (You can’t have a pointer to a reference and modify the reference—a pointer to a pointer for those of you with a C++ background.)

    Here are my study code about the ref local and return local, and I try it in VS2017, the ref variablepositionInfo can be set to fristName, so can I think the ref variable can be modified point to other location?

                    var myInfo = new List<object> { "Qiang", "Hao", 33, new { Position = "SDE3", Team = "Catelog" } };
                    //ref locals and ref returns
                    ref object positionInfo = ref GetMyPosition(myInfo.ToArray());
                    Console.WriteLine($"positionInfo = {positionInfo}");

                    var refFeild = new RefFeild();
                    ref string fristName = ref refFeild.FirstName;
                    positionInfo = fristName;
                    Console.WriteLine($"positionInfo = {positionInfo}");

    Friday, March 10, 2017 10:27 AM