ªð¦^¦Cªí ¤W¤@¥DÃD µo©«

[Âà¶K] C# 7.0 ·s¥\¯à¤¶²Ð(1/2)

[Âà¶K] C# 7.0 ·s¥\¯à¤¶²Ð(1/2)

https://blogs.msdn.microsoft.com/msdntaiwan/2017/04/10/c7-new-features/

³o½g¤å³¹¤¶²Ð¤F C# 7.0 ªº·s»yªk¡C³o¤]¬O¦b 2017/03/07 µoªíªº Visual Studio 2017 ¤¤²³¦h·s¥\¯à¤§¤@¡C

¦b C# 7.0 ·s¼W¤F³\¦h¤ä´©ªº»yªk¡A­«ÂIÂ\¦b§ïµ½®Ä¯à¡Bºë²µ{¦¡½X¡B¥H¤Î¸ê®Æ¨ú¥Î´X­Ó³¡¤À¡C¨ä¤¤³Ì¥D­nªº¥\¯à¤§¤@¬O Tuples, ¯àÅý§A§ó®e©öªº¤@¦¸¶Ç¦^¦hµ§µ²ªG¡A¥t¥~ Pattern Match ·s»yªk«h²¤Æ¤F¼¶¼g°w¹ï¯S©w¸ê®Æ«¬ºA»P±ø¥óªºµ{¦¡½X¡C°£¦¹¤§¥~¡AC# 7.0 ¤]¥]§t¤F¨ä¥L­«­nªº¦UºØ·s»yªk¤ä´©¡C§Æ±æ©Ò¦³ªº³o¨Ç§ïÅܳ£¯àÅý§A§ó´r§Öªº¼g¥X¦³®Ä²v¡A²¼äªºµ{¦¡½X¡A¦P®É¤]§ó¦³¥Í²£¤O¡C

¦pªG§A«Ü¦n©_§Ú­Ì¦p¦ó¾É¤Þ¥X³o¨Ç¥\¯àªº³]­p¹Lµ{¡A¥i¥H¬d¾\ C# Language design GitHub ºô¯¸¡A¦b¨ºÃä¥i¥H§ä¨ì³]­p»¡©ú¤å¥ó¡A³]­p´£®×¡A»P¤j¶qªº°Q½×¤º®e¡C

¦pªG§Aı±o³o½g¤å³¹¤º®e«Ü¼ô±x¡A¤]³\¬O§A´¿¸g¬Ý¹L¥h¦~¤K¤ë¥÷ (2016/08) µoªí¹Lªºª©¥»¡C¦b C# 7.0 ³Ì²×©w®×ªºª©¥»¤¤¦³¤Ö¼Æªº²§°Ê¡A³o¨Ç
²§°Ê³£¨Ó¦Û¥ý«eª©¥»ªº²³¦hÀu¨}ªº¦^õX·N¨£¡C

§Æ±æ§A³ßÅw C# 7.0, ºÉ±¡¨É¨ü¥¦, Happy Hacking !!

Mads Torgersen, C# Language PM

Ķµù:

¬°¤F§ó²M·¡ªºªí¹F³o½g¤å³¹ªº¤º®e¡A½Ķ®É§Ú±Ä¥Î·NĶ¡A¦Ó«D³v¥y½Ķ¡C§Ú¤]·|¾A®É¸É¥R¦r¥y¡AÅý¤å³¹­nªí¹Fªº·N¸q§ó²M·¡§¹¾ã¡C

¤Ó¦h±M¦³¦Wµü¡A½¦¨¤¤¤å¤Ï¦Ó¹ï¾\Ū¨S¦³À°§U¡A¦]¦¹³o³¡¤À§Ú«O¯d­ì¤å¡A¦ý¬O§Ú·|¦bĶµùªº³¡¤ÀÃB¥~¸É¥R»¡©ú¡C
´Á±æ³o¼Ë¯à§ó²M·¡ªºÅýŪªÌ¤F¸Ñ¤º®e¡C

Microsoft-logo ¥»½g¤å³¹¡A±a±z¬Ý¨ì¥H¤U C# 7.0 ·s¥\¯à¡G
Out ÅÜ¼Æ ( out variables )
Pattern Matching (¼Ò¦¡¤Ç°t )
¨Ï¥Î pattern ªº is ªí¹F¦¡
¨Ï¥Î patterns ªº switch ³¯­z¦¡
Tuples
Desconstruction (¸Ñºc )
Local functions ( °Ï°ì¨ç¦¡ )
§ï¨}ªº Literal
Ref returns »P ref locals
«D¦P¨Bªº¶Ç¦^«¬§O
§ó¼sªxªº expression bodies ¦¨­û
Throw ¹Bºâ¦¡


Out ÅÜ¼Æ (out variables)
¦b¥ý«eª©¥»ªº C# ¤¤¡Aout °Ñ¼Æªº¨Ï¥Î¨Ã¤£¦p§Ú­Ì´Á±æªº¨º»òªº¬yºZ¡C©I¥s±a¦³ out °Ñ¼Æªº method ¤§«e¡A§A¥²¶·¥ý«Å§iÅܼÆ
¨Ã¥B±N¥¦·í§@ out ªº°Ñ¼Æ¶Ç»¼¤~¦æ¡C³q±`§A¤£·| (¤]¤£»Ý­n) ¥ýªì©l¤Æ³oÅÜ¼Æ (Åܼƪº¤º®e·|¦b³Q©I¥sªº method ¤ºÂмg)¡A¦P®É§A¤]¤£¯à¨Ï¥Î var ªº¤è¦¡¨Ó«Å§i¥¦, §A¥²¶·«Ü©ú½Tªº«ü©w³oÅܼƪº§¹¾ã«¬§O:

public void PrintCoordinates(Point p)
{
    int x, y; // have to "predeclare"
    p.GetCoordinates(out x, out y);
    WriteLine($"({x}, {y})");
}
¦b C# 7.0¡A·s¼W¤F out ÅܼơA¥i¥H¦b¶Ç»¼ out °Ñ¼Æ®É¦P®É«Å§i³o­ÓÅܼÆ:

public void PrintCoordinates(Point p)
{
    p.GetCoordinates(out int x, out int y);
    WriteLine($"({x}, {y})");
}
½Ð¯d·N¡A³o­ÓÅܼƦb¥]§t¥¦¥»¨­ªº { } «Ê³¬°Ï¶ô½d³ò¤º¡A©Ò¥H±µÄò«Å§i«á­±ªºµ{¦¡½X¥i¥Hª½±µ¨Ï¥Î³o¨ÇÅܼơC
¦h¼ÆÃþ¦ü«¬ºAªº»yªk¨S¦³«ü©w¥iµø½d³ò¡A¸ÓÅܼƥiµø½d³ò´Nµ¥¦P©ó«Å§i¥Lªº°Ï¶ô½d³ò¡C

³q±` out ÅܼƳ£·|ª½±µ³Q«Å§i¬°¶Ç»¼ªº°Ñ¼Æ¡A½sĶ¾¹³q±`¯àª½±µ§P©w°Ñ¼Æªº«¬§O¬°¦ó (°£«D method ¥]§t¼Æ­Ó¤¬¬Û½Ä¬ð
ªº overloads ¦ÓµLªk§P©w)¡A¦]¦¹¥i¥Hª½±µ¨Ï¥Î var ªº¤è¦¡¨Ó«Å§i¥¦:

p.GetCoordinates(out var x, out var y);
¤@¯ë¨Ó»¡¡A§Ú­Ì±`±`¦b Try... ³oÃþªº¨Ï¥Î¼Ò¦¡¤¤¥Î¨ì out °Ñ¼Æ¡A¥¦·|¶Ç¦^ true ©Î¬O false ¨Ó¥Nªí°õ¦æ¦¨¥\»P§_¡A¦P®ÉÂÇµÛ out °Ñ¼Æ¨Ó¶Ç¦^¦¨¥\°õ¦æ«áªºµ²ªG:

public void PrintStars(string s)
{
    if (int.TryParse(s, out var i)) { WriteLine(new string('*', i)); }
    else { WriteLine("Cloudy - no stars tonight!"); }
}
¦pªG§A¤£¦b·N¬Y­Ó out °Ñ¼Æªº¶Ç¦^µ²ªG¡A¥i¥H¨Ï¥Î _ ¥Nªí©¿²¤¥¦:

p.GetCoordinates(out var x, out _); // I only care about x

Pattern Matching (¼Ò¦¡¤Ç°t)
C# 7.0 ¶}©l¤Þ¤J¤F patterns (¼Ò¦¡) ªº·§©À¡C©â¶Hªº¨Ó»¡¡A¥L¬O¥i¥H§P©w¸ê®Æ¬O§_¨ã³Æ¤@©w "§Îª¬"(Shape) ªº»yªk¤¸¯À¡A¨Ã±q¸Ó¼Æ­È¤§¤¤´£¨ú»Ý­nªº¸ê°T¡C

Ķµù:
Shape, ¥Nªí¸ê®Æªº "§Îª¬", ºë½Tªº¨Ó»¡¥]§t¸ê®Æ¥»¨­«¬§O¥]§t­þ¨Ç¦¨­û? ³o¨Ç¦¨­ûªº¼Æ­È¬O§_¸¨¦b¹w´Áªº½d³ò?
patterns ¥i¥HÅý§PÂ_¸ê®Æ "§Îª¬" ªºµ{¦¡½X§ó¬°Â²¼ä©ú½T¡C

Á|¨Ò¨Ó»¡¡AC# 7.0 ¤ä´©ªº patterns ¦³³o´XºØ:

Constant Patterns (±`¼Æ¼Ò¦¡, ¥H c ªí¥Ü¡Ac ¬O C# ªº±`¼Æªí¹F¦¡), ´ú¸Õ¿é¤Jªº¼Æ­È¬O§_»P c ¬Ûµ¥¡C
Type Patterns (Ãþ«¬¼Ò¦¡, ¥H T x ªí¥Ü¡AT ¥Nªí«¬§O¡A¦Ó x ¬OÃѧO¦WºÙ), ´ú¸Õ¿é¤Jªº¼Æ­È¬O§_ÄÝ©óÃþ§O T? ¦pªG¬Oªº¸Ü´N§â¿é¤Jªº¼Æ­È©ñ¨ìÃþ«¬¬° T ªºÅÜ¼Æ x ¤¤¡C
Var Patterns (ÅܼƼҦ¡, ¥H var x ªí¥Ü, x ¬OÃѧO¦WºÙ), ³oºØ¼Ò¦¡¤U¥Ã»··|¤Ç°t¦¨¥\¡A¦¹®É x ªº«¬§O»P¿é¤Jªº¼Æ­È¬Û¦P¡A³o¼Ò¦¡¤U¥u¬O²³æªº§â¿é¤Jªº¼Æ­È©ñ¨ì x ¤§¤¤¡C
³o¨Ç¥u¬O­pµe¤¤ªº²Ä¤@¨B - pattern (¼Ò¦¡) ¬O C# ·s«¬ºAªº»yªk¤¸¯À¡A§Ú­Ì´Á±æ¥¼¨Ó¯àÄ~Äò·s¼W§ó¦hªº¥\¯à¡C

¦b C# 7.0 §Ú­Ì¥Î pattern ¨Ó¼W±j¨âºØ¬J¦³ªº»yªkµ²ºc:

is expression (is ªí¹F¦¡) ²{¦b¥i¥H¦b¥k¤è¥[¤W pattern¡A¦b¤§«e«h¥u¯à©w¸q«¬§O¡C
switch ³¯­z¦¡¤¤ªº case ¤l¥y¡A²{¦b¥i¥H¤ñ¹ï¼Ò¦¡¬O§_²Å¦X¡A¹L¥h«h¥u¤ä´©±`¼Æ¼Æ­È¡C
¦b¥¼¨Óªº C# §Ú­Ì·|¼W¥[§ó¦h¾A¥Î pattern ªº»yªk¡C

¨Ï¥Î pattern ªº is ªí¹F¦¡
¨Ó¬Ý¬Ý¨Ï¥Î constant patterns »P type patterns ªº is expression ¨Ï¥Î½d¨Ò:

public void PrintStars(object o)
{
    if (o is null) return;     // constant pattern "null"
    if (!(o is int i)) return; // type pattern "int i"
    WriteLine(new string('*', i));
}
¦p©Ò¨£¡Apattern ÅÜ¼Æ - ¥Ñ pattern ¤Þ¤JªºÅܼơA¸ò«e­±¤¶²Ðªº out ÅܼƫD±`¬Û¦ü¡A§A¥i¥H«Å§i¦bªí¹F¦¡¤§¤¤¡A¦Ó¥B¥i¥Hª½±µ´Nªñ¦b¦P¥i¬O½d³ò¤ºª½±µ¨Ï¥Î¥L¡C

¸ò out Åܼƫܬۦüªº¦a¤è¬O¡A¼Ò¦¡ÅܼƬO¥iÅܰʪº¡A§Ú­Ì±`±N out ÅܼƻP pattern ÅܼơA²ÎºÙ¬° expression ÅܼơC

Patterns ±`»P Try... method ¤@°_¨Ï¥Î:

if (o is int i || (o is string s && int.TryParse(s, out i)) { /* use i */ }

¨Ï¥Î patterns ªº switch ³¯­z¦¡
¦b C# 7.0¡A§Ú­Ì¤]ÂX¤j¤F switch ³¯­z¦¡ªºÀ³¥Î½d³ò:

switch ³¯­z¦¡²{¦b¥i¥H¹B¥Î¦b©Ò¦³«¬§O (¤£¦A¥u­­©ó°ò¥»Ãþ«¬)
patterns ¥i¥H¥Î¦b case ¤l¥y
case ¤l¥y¥i¥Hªþ¥[±ø¥ó§PÂ_¦¡
³oÃ䦳¹ïÀ³ªº½d¨Òµ{¦¡½X:

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));
}
³o¸Ì¦³´X­Ó switch ³¯­z¦¡·s¼WªºÂX¥R¥\¯à:

case ¤l¥yªº¶¶§Ç¬O­«­nªº:
´N¦p¦P catch ¤l¥y¤@¼Ë¡A¦h­Ó case ¤l¥y¤§¶¡¤£¦A¬O¨S¦³¶¶§ÇÃöÁpªº¡A¦Ó²Ä¤@­Ó²Å¦X±ø¥óªº case ¤l¥y·|³Q¿ï¤¤¡C³oÂI«D±`­«­n¡A®³¤W¤@­Ó½d¨Òµ{¦¡½X¨Ó»¡¡A¥Nªí¥¿¤è§Îªº³o­Ó case ¤l¥y (Ķµù: case Rectangle s when (s.Length == s.Height):) À³¸Ó­n±Æ¦b¥Nªíªø¤è§Îªº case ¤l¥y (case Rectangle r:) «e­±¡Aµ²ªG¤~·|¥¿½T¡C¥t¥~¡A´N¹³ catch ¤l¥y¤@¼Ë¡A½sĶ¾¹¥i¥H¼Ð¥Ü¥X¥Ã»·µLªk°õ¦æ¨ìªºµ{¦¡½X¨Ó¨ó§U§A¡C¦b³o¤§«e¡A§AµLªk¤]¤£»Ý­n«ü©w¦h­Ó case ¤§¶¡ªºµû¦ô¶¶§Ç¡A©Ò¥H³o¨Ã¤£¬O­Ó¯}Ãa©Êªº§ïÅÜ (breaking change)¡C
default ¤l¥y¥Ã»··|³Ì«á¤~µû¦ô:
§Y¨Ï¦b¤W­zªº¨Ò¤l¤¤¡Anull case ¤l¥y³QÂ\¦b³Ì«á¡A¥L¤´µM·|¦b default ¤l¥y¤§«e³QÀˬd¡C³o¼Ëªº³]­p¬O¬°¤F»P²{¦³ªº switch ³¯­z¥y«O«ù¬Û®e¡CµM¦Ó¡A¦nªº°µªk³q±`·|©ú½Tªº±N default ¤l¥yÂ\¦b³Ì«á­±¡C
Â\¦b³Ì«á­±ªº null case ¤l¥y¨Ã¤£·|µLªk³Q³Q°õ¦æ¨ì:
¦]¬° type patterns (Ãþ«¬¼Ò¦¡) ¨Ì´` is expression ªº¨Ò¤l¡A¤£·|»P null ¤l¥y¤Ç°t¡C³o¥i¥H½T«O null ¤l¥y¤£·|¤£¤p¤ß³Q¥ô¦óªº type patterns (Ãþ«¬¼Ò¦¡) µ¹·m¨«¡A§A¥²¶·§ó²M·¡¸Ó¦p¦ó³B²z³oºØª¬ªp (©Î¬O§â¥¦¯dµ¹ default ¤l¥y¨Ó³B²z)¡C
¥Ñ case ... ¤Þ¶iªº pattern ÅÜ¼Æ ¡A¥Lªº¥iµø½d³ò¥u­­©ó¹ïÀ³ªº switch °Ï¬q¡C

Tuples
·Q­n±q¤@­Ó method ¶Ç¦^¤@­Ó¥H¤Wªº¶Ç¦^­È¬OÆZ±`¨£ªºª¬ªp¡C¦ý¬O¥Ø«e C# ª©¥»¹ï³o»Ý¨D¯à´£¨Ñªº§@ªk³£¤£°÷¦n¡C²{¦³ªº§@ªk¦³:

out °Ñ¼Æ:
¨Ï¥Î¤W«Ü²ÖÂØ (§Y¨Ï¦b«e­±ªº³¡¤À¤w¸g¤¶²Ð¤F§ï¨}ªº»yªk)¡A¦Ó¥B³o¤è¦¡¤]µLªk·f°t async method ¤@°_¨Ï¥Î¡C
¨Ï¥Î System.Tuple<...> «¬§O¨Ó¶Ç¦^­È:
»Ý­n¤â°Ê°t¸m¤@­Ó tuple ª«¥ó¡A¦P®É¤]»Ý­n¼g¨Ç¤¾ªøªº code ¤~¯à¿ì¨ì¡C
´À¨C­Ó method ³£¦Û­q±MÄݪº¶Ç¦^­È«¬§O:
±oÃB¥~¼g¤j¶qªº code ¨Ó§¹¦¨³o¥ó¨Æ¡A¦ý¬O¥Øªº¥u¬O¼È®É±N¦h­Ó¼Æ­È²Õ¦X°_¨Ó¦Ó¤w¡C
¨Ï¥Î dynamic ¨Ó¶Ç¦^°Î¦Wªº«¬§O (anonymous types):
µLªk°µ¨ìÀRºA«¬§OÀˬd¡A¦P®É±N·|¥I¥X«Ü°ªªº®Ä¯à¥N»ù¡C
¬°¤FÅý³o¥ó¨Æ°µ±o§ó¦n¡AC# 7.0 ·s¼W¤F tuple types ¤Î tuple literals ªº»yªk:

(string, string, string) LookupName(long id) // tuple return type
{
    ... // retrieve first, middle and last from data storage
    return (first, middle, last); // tuple literal
}
³o method ²{¦b¯à§ó¦³®Ä²vªº¶Ç¦^¤T­Ó¦r¦ê«¬§Oªº¶Ç¦^­È¤F¡A³o½d¨Ò±N¤T­Ó¦r¦ê¥]¦¨¤@­Ó tuple¡C
©I¥s³o method ªºµ{¦¡½X±N·|¦¬¨ì¦^¶Çªº tuple ª«¥ó¡A¥B¯à³z¹L tuple ª«¥ó­Ó§O¦s¨ú³o¨Ç«Ê¸Ë¦b¤ºªº¸ê®Æ:

var names = LookupName(id);
WriteLine($"found {names.Item1} {names.Item3}.");
¨ä¤¤ Item1 µ¥µ¥¡A¬° tuple ¤ºªº¤¸¯À¹w³]ªº¦WºÙ¡A³o¤èªk¯à¥¿±`¹B§@, ¦ý¬O³o©R¦W¤è¦¡²×¨s¤£¯à«Ü¯à²M·¡ªí¹F¥Î³~¡C©Ò¥H§AÄ@·Nªº¸Ü¥i¥H©ú½Tªº´À¥¦­Ì«ü©w§ó¾A¦Xªº¦WºÙ:

(string first, string middle, string last) LookupName(long id) // tuple elements have names
²{¦b³o­Ó tuple ªº¤¸¯À¯à¥Î§ó¶K¤Áªº¦WºÙ¨Ó¦s¨ú¤§¤ºªº¤¸¯À¤F:

var names = LookupName(id);
WriteLine($"found {names.first} {names.last}.");
§A¤]¥i¥Hª½±µ¦b tuple literals ¤º«ü©w¤¸¯Àªº¦WºÙ:

    return (first: first, middle: middle, last: last); // named tuple elements in a literal
¤@¯ë¨Ó»¡¡A§A¥i¥H¤¬¬Û«ü¬£ tuple ªºÅܼơA¦Ó¤£¥ÎºÞ¥Lªº¦WºÙ¬°¦ó: ¥u­n­Ó§Oªº¤¸¯À³£¥i¥H³Q«ü¬£¡Atuple «¬§O¥i¥H¦Û¥ÑÂà´«¬°¨ä¥Lªº tuple «¬§O¡C

Tuples ¬O value types, ¦Ó¥B¥¦¥]§tªº¤¸¯À³£«Ü³æ¯Âªº³Q¼Ð¥Ü¬° public, ³£¬O¥i²§°ÊªºÄæ¦ì (mutable fields)¡C¥¦­Ì¬O "¼Æ­È¬Ûµ¥" (value equality) ªº¡A
·N«ä¬O¥u­n¨â­Ó tuples ªº©Ò¦³¹ïÀ³ªº¤¸¯À³£¬O¬Ûµ¥ªº (¦Ó¥B hash code ¤]¥²¶·¬Û¦P)¡A¨º³o¨â­Ó tuples ´N¬O¬Ûµ¥ªº (hash code ¤]·|¬Û¦P) ¡C

°£¤F¶Ç¦^¦h­Ó¶Ç¦^­Èªº±¡ªp¤§¥~¡A¦b¨ä¥L¦a¤è tuples ¤]«Ü¦³¥Î¡C¨Ò¦p¡A¦pªG§A»Ý­n¤@­Ó¥]§t¦h­Ó Key ªº Dictionary¡A§A¥u»Ý­n®³ tuple ·í§@ Dictionary ªº Key ´N¥i¥H¤F¡C¦pªG§A»Ý­n¦b List ¤ºªº¤@­Ó¤¸¯À©ñ¸m¦h­Ó¤£¦Pªº¼Æ­È¡A¥u­n¨Ï¥Î tuple «¬§O¨Ã¥B·j´M³o­Ó List¡C¦b³o¨Ç±¡ªp¤¤¡Atuple ³£¯à¥¿±`¹B§@¡C

Tuples ªº¹ê§@¥²¶·¨Ì¾a©³¼hªºªx«¬µ²ºc«¬§O (generic struct types): ValueTuple<...>¡C¦pªG§A¨Ï¥Îªº target framework ª©¥»ÁÙ¥¼¥]§t¥¦¡A§A¥u»Ý­n³z¹L NuGet ¨ú±o¥L­Ì§Y¥i:

¦b "¤è®×Á`ºÞ" ¤ºªº "±M®×" ¤W«ö¥kÁä¡A¿ï¾Ü "ºÞ²z NuGet ®M¥ó..."
¿ï¾Ü "ÂsÄý" ­¶ÅÒ¡A¦P®É¦b "®M¥ó¨Ó·½" ¶µ¥Ø¤¤¿ï¾Ü "nuget.org"
·j´M "System.ValueTuple" ¨Ã¦w¸Ë

        ÀR«ä¦Û¦b : ¹D¼w¬O´£ª@¦Û§Úªº©ú¿O¡A¤£¸Ó¬O¨þ¥¸§O¤HªºÃ@¤l¡C
ªð¦^¦Cªí ¤W¤@¥DÃD